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 dynamic
s 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
Send Keys Through Sendinput in User32.Dll
What's a Good Threadsafe Singleton Generic Template Pattern in C#
Selectively Suppress Custom Obsolete Warnings
C# Generic "Where Constraint" with "Any Generic Type" Definition
String.Replace() VS. Stringbuilder.Replace()
How to Data Bind a List of Strings to a Listbox in Wpf/Wp7
Does ASP.NET MVC Have Application Variables
Microsoft.Office.Interop.Excel Really Slow
How to Give Read/Write Permissions to a Folder During Installation Using .Net
Fields of Class, Are They Stored in the Stack or Heap
Is There Any Connection String Parser in C#
Suppress Properties with Null Value on ASP.NET Web API
Using Msbuild to Execute a File System Publish Profile
What Are the Benefits of Resource(.Resx) Files