Return/Consume Dynamic Anonymous Type Across Assembly Boundaries

Return/consume dynamic anonymous type across assembly boundaries

Use an ExpandoObject instead of an anonymous type. This should allow you to cross assembly boundaries safely:

public static dynamic GetPerson()
{
dynamic person = new ExpandoObject();
person.Name = "Foo";
person.Age = 30;

return person;
}

In general, anonymous types should really only be used within the same method in which they are generated. Returning an anonymous type from a method is, in general, going to cause more problems than it solves.

Accessing properties of anonymous/dynamic types across dll boundaries gives RuntimeBinderException

Anonymous types are internal to the assembly they are created in. If you have control over the source code you can make them Friend Assemblies

[assembly:InternalsVisibleTo("TheOtherAssembly")]

but there are drawbacks.

Why can't I access properties of an anonymous type returned from a function via the dynamic keyword?

The problem is that anonymous types are internal, which means that you can't access their properties with dynamic property accessors from projects other than the one they were created in. The dynamic binding treats them as the closest public inherited type it knows about--object.

To fix this, you can declare a public type to represent the values you're expecting to find in your anonymous type. This is probably a good idea anyway, since you're clearly expecting to consume the returned properties in other parts of your code. Using a declared type also enables you to maintain type-safety, avoiding the need for dynamic entirely.

If you absolutely must use dynamics here, the next best option is probably to change your AssemblyInfo.cs file to make internal properties accessible to the project you're trying to access them from:

[assembly:InternalsVisibleTo("MyOtherProject")]

Returning anonymous type in C#

You can't.

You can only return object, or container of objects, e.g. IEnumerable<object>, IList<object>, etc.

How to dynamic new Anonymous Class?

Anonymous types are just regular types that are implicitly declared. They have little to do with dynamic.

Now, if you were to use an ExpandoObject and reference it through a dynamic variable, you could add or remove fields on the fly.

edit

Sure you can: just cast it to IDictionary<string, object>. Then you can use the indexer.

You use the same casting technique to iterate over the fields:

dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;

foreach (var property in (IDictionary<string, object>)employee)
{
Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33

The above code and more can be found by clicking on that link.

Proxying Anonymous Objects across AppDomain boundaries

Ok. Assuming you know about reflection and creating a new AppDomain. Which I know you know how to do... :)

I've created two helper classes that will allow you to pass anonymous objects. ProxyAnonymousObject and ProxyDynamicObject. You create the ProxyAnonymousObject in the first AppDomain and use the ProxyDynamicObject in the other AppDomain. (both of these objects exist in the primary AppDomain library)

[Serializable]
public class ProxyAnonymousObject : ISerializable {

static Dictionary<string, Type> cached = new Dictionary<string, Type>();

object model;

public Dictionary<string, object> ModelProperties = new Dictionary<string, object>();

public ProxyAnonymousObject(object model) { this.model = model; }
public ProxyAnonymousObject(SerializationInfo info, StreamingContext ctx) {
try {

string fieldName = string.Empty;
object fieldValue = null;

foreach (var field in info) {
fieldName = field.Name;
fieldValue = field.Value;

if (string.IsNullOrWhiteSpace(fieldName))
continue;

if (fieldValue == null)
continue;

ModelProperties.Add(fieldName, fieldValue);

}

} catch (Exception e) {
var x = e;
}
}

public void GetObjectData(SerializationInfo info, StreamingContext context) {

foreach (var pi in model.GetType().GetProperties()) {
info.AddValue(pi.Name, pi.GetValue(model, null), pi.PropertyType);
}

}
}

public class ProxyDynamicObject : DynamicObject{
internal ProxyAnonymousObject Proxy { get; set; }

public ProxyDynamicObject(ProxyAnonymousObject model) {
this.Proxy = model;
}

public override bool TryGetMember(GetMemberBinder binder, out object result) {
result = Proxy.ModelProperties[binder.Name];
return true;
}
}

To get this to work in your MarshalByRefObject inherited class you just set the target dynamic object equal to a new ProxyDynamicObject(model). In the example I've written up I make a call like this.

instance = Activator.CreateInstance(type);
var setModel = type.GetMethod("SetModel", BindingFlags.Public | BindingFlags.Instance);
var render = type.GetMethod("Render", BindingFlags.Public | BindingFlags.Instance);

setModel.Invoke(instance, new object[] { new ProxyDynamicObject(model) });
render.Invoke(instance, null);

I've written a blog post about it http://buildstarted.com/2011/06/28/getting-anonymous-types-to-cross-the-appdomain-boundary/ to explain it in a bit more detail. (though it's something I'm not very good at)

There are definitely problems with this implementation. It doesn't support nested Anonymous Types and I'm fairly certain it will break in general. But it's definitely something to get you on the right track.



Related Topics



Leave a reply



Submit