Serializing a IKVMC generated object to JSON - c#

I have a java library that contains all the domain models for our backend REST API. The backend API is implemented in Java and translate Java objects to JSON using Jackson.
Recently we need to implement a new feature and have a Windows .NET application talk to our API.
However, since the domain model (contract) is all in Java, we had to translate all the Java classes to C# classes so that we can use Json.NET to serialize/deserialize JSON, but this quickly became time-consuming. In addition, whenver there is contract change in Java we likely have to do this for C# classes also.
I searched online and found out IKVMC can translate a jar to a DLL, so I tried it out, however, it's causing some Json.NET serialization problems.
For example
I have a Java object that looks like this:
public class ApiToken {
private String apiKey;
private String apiSecret;
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public String getApiSecret() {
return apiSecret;
}
public void setApiSecret(String apiSecret) {
this.apiSecret = apiSecret;
}
#Override
public int hashCode() {
return Objects.hashCode(apiKey, apiSecret);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ApiToken other = (ApiToken) obj;
return Objects.equal(this.apiKey, other.apiKey) && Objects.equal(this.apiSecret, other.apiSecret);
}
#Override
public String toString() {
return Objects.toStringHelper(this).add("apiKey", apiKey).add("apiSecret", apiSecret).toString();
}
}
AFTER translation by ikvmc, it looks like this:
public class ApiToken : Object
{
[LineNumberTable(11)]
[MethodImpl(MethodImplOptions.NoInlining)]
public ApiToken();
public virtual string getApiKey();
public virtual void setApiKey(string apiKey);
public virtual string getApiSecret();
public virtual void setApiSecret(string apiSecret);
[LineNumberTable(35)]
public override int hashCode();
[LineNumberTable(new byte[] {159, 182, 100, 98, 99, 98, 110, 98, 103})]
[MethodImpl(MethodImplOptions.NoInlining)]
public override bool equals(object obj);
[LineNumberTable(52)]
public override string toString();
}
when I create this object in C#, JSON.net does NOT seralize this object correctly. Instead, it just produces an empty JSON {}
I suspect this is because there are no fields/properties exposed in the object that is generated by ikvmc.
Does anyone know if there's a workaround for this?
Thanks a lot and much appreciated
Update:
This is how I'm serializing the object
ApiToken apiToken = new ApiToken();
apiToken.setApiKey("test");
apiToken.setApiSecret("secret");
string json = JsonConvert.SerializeObject(apiToken);
The json output is {}.

I suspect this is because there are no fields/properties exposed in the object that is generated by ikvmc
Yes.
Does anyone know if there's a workaround for this?
You can do it by writing a custom ContractResolver and ValueProvider as below
var obj = new ApiToken();
obj.setApiKey("X-X-X");
obj.setI(666);
var settings = new Newtonsoft.Json.JsonSerializerSettings() {
ContractResolver = new MyContractResolver()
};
var json = JsonConvert.SerializeObject(obj, settings);
//json : {"ApiKey":"X-X-X","I":666}
var newobj = JsonConvert.DeserializeObject<ApiToken>(json, settings);
//Test class
public class ApiToken
{
private String apiKey;
public String getApiKey()
{
return apiKey;
}
public void setApiKey(String apiKey)
{
this.apiKey = apiKey;
}
private int i;
public int getI()
{
return i;
}
public void setI(int i)
{
this.i = i;
}
public string dummy()
{
return "abcde";
}
}
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
//Find methods. setXXX getXXX
var properties = type.GetMethods()
.Where(m => m.Name.Length > 3)
.GroupBy(m => m.Name.Substring(3))
.Where(g => g.Count() == 2 && g.Any(x=>x.Name=="set" + g.Key) && g.Any(x=>x.Name=="get" + g.Key))
.ToList();
//Create a JsonProperty for each set/getXXX pair
var ret = properties.Select(prop=>
{
var jProp = new Newtonsoft.Json.Serialization.JsonProperty();
jProp.PropertyName = prop.Key;
jProp.PropertyType = prop.First(m => m.Name.StartsWith("get")).ReturnType;
jProp.ValueProvider = new MyValueProvider(prop.ToList());
jProp.Readable = jProp.Writable = true;
return jProp;
})
.ToList();
return ret;
}
}
public class MyValueProvider : Newtonsoft.Json.Serialization.IValueProvider
{
List<MethodInfo> _MethodInfos = null;
public MyValueProvider(List<MethodInfo> methodInfos)
{
_MethodInfos = methodInfos;
}
public object GetValue(object target)
{
return _MethodInfos.First(m => m.Name.StartsWith("get")).Invoke(target, null);
}
public void SetValue(object target, object value)
{
_MethodInfos.First(m => m.Name.StartsWith("set")).Invoke(target, new object[] { value });
}
}

Related

How do I add lexicographical sort in this System.Text.Json serialization way?

I'm implementing an exchange which has private endpoints which are protected with a signature. The first code looks better but it results into an invalid signature due to the fact that it doesn't lexicographically sort it. How do I fix it? The second code works fine btw.
Official Java example - here
Docs
Broken code
public ValueTask SubscribeToPrivateAsync()
{
var request = JsonSerializer.Serialize(new MexcSubPersonalPayload(_apiKey, _apiSecret));
return SendAsync(request);
}
internal class MexcSubPersonalPayload
{
private readonly string _apiSecret;
public MexcSubPersonalPayload(string apiKey, string apiSecret)
{
ApiKey = apiKey;
_apiSecret = apiSecret;
}
[JsonPropertyName("op")]
[JsonPropertyOrder(1)]
public string Operation => "sub.personal";
[JsonPropertyName("api_key")]
[JsonPropertyOrder(2)]
public string ApiKey { get; }
[JsonPropertyName("sign")]
[JsonPropertyOrder(3)]
public string Signature => Sign(ToString());
[JsonPropertyName("req_time")]
[JsonPropertyOrder(4)]
public long RequestTime => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
private string Sign(string payload)
{
if (string.IsNullOrWhiteSpace(payload))
{
return string.Empty;
}
using var md5 = MD5.Create();
var sourceBytes = Encoding.UTF8.GetBytes(payload);
var hash = md5.ComputeHash(sourceBytes);
return Convert.ToHexString(hash);
}
public override string ToString()
{
return $"api_key={HttpUtility.UrlEncode(ApiKey)}&req_time={RequestTime}&op={Operation}&api_secret={HttpUtility.UrlEncode(_apiSecret)}";
}
}
Working code
public ValueTask SubscribeToPrivateAsync()
{
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var #params = new Dictionary<string, object>
{
{ "api_key", _apiKey },
{ "req_time", now },
{ "op", "sub.personal" }
};
var signature = SignatureHelper.Sign(SignatureHelper.GenerateSign(#params, _apiSecret));
var request = new PersonalRequest("sub.personal", _apiKey, signature, now);
return SendAsync(request);
}
internal static class SignatureHelper
{
public static string GenerateSign(IDictionary<string, object> query, string apiSecret)
{
// Lexicographic order
query = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
var sb = new StringBuilder();
var queryParameterString = string.Join("&",
query.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value.ToString()))
.Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value.ToString())}"));
sb.Append(queryParameterString);
if (sb.Length > 0)
{
sb.Append('&');
}
sb.Append("api_secret=").Append(HttpUtility.UrlEncode(apiSecret));
return sb.ToString();
}
public static string Sign(string source)
{
using var md5 = MD5.Create();
var sourceBytes = Encoding.UTF8.GetBytes(source);
var hash = md5.ComputeHash(sourceBytes);
return Convert.ToHexString(hash);
}
}
As explained in Configure the order of serialized properties, as of .NET 6 the only mechanism that System.Text.Json provides to control property order during serialization is to apply [JsonPropertyOrder] attributes to your model [1]. You have already done so -- but the order specified is not lexicographic. So what are your options?
Firstly, you could modify your model so that the [JsonPropertyOrder] are in lexicographic order:
internal class MexcSubPersonalPayload
{
private readonly string _apiSecret;
public MexcSubPersonalPayload(string apiKey, string apiSecret)
{
ApiKey = apiKey;
_apiSecret = apiSecret;
}
[JsonPropertyName("op")]
[JsonPropertyOrder(2)]
public string Operation => "sub.personal";
[JsonPropertyName("api_key")]
[JsonPropertyOrder(1)]
public string ApiKey { get; }
[JsonPropertyName("sign")]
[JsonPropertyOrder(4)]
public string Signature => Sign(ToString());
[JsonPropertyName("req_time")]
[JsonPropertyOrder(3)]
public long RequestTime => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Remainder unchanged
Demo fiddle #1 here.
Secondly, if you cannot modify your [JsonPropertyOrder] attributes (because e.g. you require a different order in a different context), then as long as you are using .NET 6 or later, you could serialize your model to an intermediate JsonNode hierarchy and sort its properties before finally formatting as a JSON string. To do this, first introduce the following extension methods:
public static partial class JsonExtensions
{
public static JsonNode? SortProperties(this JsonNode? node, bool recursive = true) => node.SortProperties(StringComparer.Ordinal, recursive);
public static JsonNode? SortProperties(this JsonNode? node, IComparer<string> comparer, bool recursive = true)
{
if (node is JsonObject obj)
{
var properties = obj.ToList();
obj.Clear();
foreach (var pair in properties.OrderBy(p => p.Key, comparer))
obj.Add(new (pair.Key, recursive ? pair.Value.SortProperties(comparer, recursive) : pair.Value));
}
else if (node is JsonArray array)
{
foreach (var n in array)
n.SortProperties(comparer, recursive);
}
return node;
}
}
And then you will be able to do:
public string GenerateRequest() => JsonSerializer.SerializeToNode(new MexcSubPersonalPayload(_apiKey, _apiSecret))
.SortProperties(StringComparer.Ordinal)!
.ToJsonString();
Demo fiddle #2 here.
Thirdly, you could create a custom JsonConverter<MexcSubPersonalPayload> that serializes the properties in the correct order, e.g. by mapping MexcSubPersonalPayload to a DTO, then serializing the DTO. First define:
class MexcSubPersonalPayloadConverter : JsonConverter<MexcSubPersonalPayload>
{
record MexcSubPersonalPayloadDTO(
[property:JsonPropertyName("api_key"), JsonPropertyOrder(1)] string ApiKey,
[property:JsonPropertyName("op"), JsonPropertyOrder(2)] string Operation,
[property:JsonPropertyName("req_time"), JsonPropertyOrder(3)]long RequestTime,
[property:JsonPropertyName("sign"), JsonPropertyOrder(4)]string Signature);
public override void Write(Utf8JsonWriter writer, MexcSubPersonalPayload value, JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, new MexcSubPersonalPayloadDTO(value.ApiKey, value.Operation, value.RequestTime, value.Signature), options);
public override MexcSubPersonalPayload Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
}
And then do:
public string GenerateRequest() => JsonSerializer.Serialize(new MexcSubPersonalPayload(_apiKey, _apiSecret), new JsonSerializerOptions { Converters = { new MexcSubPersonalPayloadConverter() }});
Demo fiddle #3 here.
[1] With Json.NET one could override property order via a custom contract resolver, but as of .NET 6 System.Text.Json lacks this flexibility as its contract information is private.

MongoDB .NET - Get IMongoCollection by Name

I'm refactoring some legacy code away from the MongoDB.Driver.Legacy API.
I've got the following method, which gets a MongoCollection by string collection name.
protected virtual MongoCollection GetMongoCollection(Type type)
{
return Store.GetCollection(GetCollectionName(type));
}
Store in this example is a MongoDatabase from the Legacy API. The GetCollectionName() method looks up various things in BsonClassMap to determine the string name of the collection:
private string GetCollectionName(Type type)
{
return !IsRegisteredWithClassMap(type) ? type.Name : GetRegisteredClassMapType(type).Name;
}
private Type GetRegisteredClassMapType(Type objectType)
{
if (objectType.BaseType == null)
{
return null;
}
var isroot = BsonClassMap.LookupClassMap(objectType.BaseType).IsRootClass;
return isroot ? objectType.BaseType : GetRegisteredClassMapType(objectType.BaseType);
}
private bool IsRegisteredWithClassMap(Type objectType)
{
var isRegistered = GetRegisteredClassMapType(objectType);
return isRegistered != null;
}
How would I implement the GetMongoCollection() method using the new API. The IMongoDatabase from the new API doesn't have a GetCollection() method which accepts a string. Only a Generic version GetCollection<T>
If you set a discrimitor to the class (custom collection name), you can use this:
[BsonDiscriminator("CollectionName")]
public class MyModel
{
...
}
var client = new MongoClient("connection string");
var database = client.GetDatabase("database name");
var collection = database.GetCollection<MyModel>(BsonClassMap.LookupClassMap(typeof(MyModel)).Discriminator)
try this:
protected virtual MongoCollection GetMongoCollection(Type type)
{
var method = typeof(IMongoDatabase).GetMethod("GetCollection");
var result = method.MakeGenericMethod(type).Invoke(yourMongoDbInstance);
return (MongoCollection) result;
}
if you can afford more effort just change the previous method signature in this way:
protected virtual MongoCollection GetMongoCollection<T>() {
return yourMongoDbInstance.GetCollection<T>();
}

How to serialize/deserialze a class that implements IEnumerator, IEnumerable?

Just want to stress out this question is irrelevant of why this technology and why not that. This is related to C# code.
I searched below but none provides solution.
Serialize a class that implements IEnumerable
Serialization on classes that implement IEnumerator
I am working on WCF Rest project in C# which is replacing .NET Remoting communication layer. I use Newtonsoft dll to serialize and de-serialize.
I have a class called MyDTO which implements IEnumerator, IEnumerable which I cannot change since this is old class and many applications in production use them. When I try to serialize MyDTO into string I do not get any error/exception message, I just get an empty array like "[]". Can anybody tell how we can serialize/deserialze class that implements IEnumerator, IEnumerable?
public class MyDTO : IEnumerator, IEnumerable
I am calling a method called ABC in OldServer.dll which gives me MyDTO object. I want to convert this to string and again from string to MyDTO.
Please let me know if you need more information. Please see below for MyDTO class which I cannot change:
[Serializable]
public class MyDTO : IEnumerator, IEnumerable
{
#region Inner Types
internal class MyObjComparer : IComparer<MyObj>
{
public int Compare(MyObj x, MyObj y)
{
return x.InputCode.CompareTo(y.InputCode);
}
}
#endregion
#region Variables
private List<MyObj> myObjList;
private string selectedObjectMessage;
private bool containSequenceNo = false;
private bool sortEnabled;
private bool filterEnabled;
private IComparer<MyObj> objectComparer;
#endregion
#region Constructors
public MyDTO()
{
this.myObjList = new List<MyObj>();
this.selectedObjectMessage = string.Empty;
}
public MyDTO(List<MyObj> objects)
{
this.myObjList = objects;
this.selectedObjectMessage = string.Empty;
}
public MyDTO(IComparer<MyObj> argSortComparer)
: this()
{
this.objectComparer = argSortComparer;
}
public MyDTO(List<MyObj> argErrors, IComparer<MyObj> argSortComparer)
: this(argErrors)
{
this.objectComparer = argSortComparer;
}
public MyDTO(List<MyObj> argErrors, IComparer<MyObj> argSortComparer, bool argSortEnabled)
: this(argErrors, argSortComparer)
{
this.sortEnabled = argSortEnabled;
}
#endregion
#region Properties
public string selectedObjectMessage
{
get { return this.selectedObjectMessage; }
set
{
if (value == null)
value = string.Empty;
this.selectedObjectMessage = value;
}
}
public int Count
{
get { return this.myObjList.Count; }
}
public bool ErrorsContainsSequenceNo
{
get { return this.containSequenceNo; }
set { this.containSequenceNo = value; }
}
public List<MyObj> myObjList
{
get { return this.myObjList; }
set { this.myObjList = value; }
}
public MyDTO WithoutEmptyMyObjects
{
get
{
MyDTO objs = new MyDTO();
foreach (MyObj obj in this.myObjList)
{
if (!string.IsNullOrEmpty(obj.InputCode))
objs.Add(obj);
}
return objs;
}
}
#endregion
}
UPDATE 1:
After spending almost a day we decided to write our own method targeting MyDTO which will produce XmlDocument which is serialize. Using that same XmlDocument we will try create MyDTO object. I feel we need to just concentrate on properties that has setters. I will let you know.
Using the almighty Json.Net
1) Create a contract resolver to serialize/deserialize specific members based on their accessibility, cause by default serialization/deserialization only happens for public members.
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
2) Then you can serialize your object like
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
// dtoInstance is your dto instance you wish to serialize.
string serializedDTO = JsonConvert.SerializeObject(dtoInstance, settings);
3) And you can deserialize like
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
MyDTO deserializedDTO = JsonConvert.DeserializeObject<MyDTO>(serializedDTO, settings);

How to get the name of <T> from generic type and pass it into JsonProperty()?

I get the following error with the code below:
"An object reference is required for the non-static field, method, or
property 'Response.PropName'"
Code:
public class Response<T> : Response
{
private string PropName
{
get
{
return typeof(T).Name;
}
}
[JsonProperty(PropName)]
public T Data { get; set; }
}
What you're trying to do is possible, but not trivial, and can't be done with only the built-in attributes from JSON.NET. You'll need a custom attribute, and a custom contract resolver.
Here's the solution I came up with:
Declare this custom attribute:
[AttributeUsage(AttributeTargets.Property)]
class JsonPropertyGenericTypeNameAttribute : Attribute
{
public int TypeParameterPosition { get; }
public JsonPropertyGenericTypeNameAttribute(int position)
{
TypeParameterPosition = position;
}
}
Apply it to your Data property
public class Response<T> : Response
{
[JsonPropertyGenericTypeName(0)]
public T Data { get; set; }
}
(0 is the position of T in Response<T>'s generic type parameters)
Declare the following contract resolver, which will look for the JsonPropertyGenericTypeName attribute and get the actual name of the type argument:
class GenericTypeNameContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<JsonPropertyGenericTypeNameAttribute>();
if (attr != null)
{
var type = member.DeclaringType;
if (!type.IsGenericType)
throw new InvalidOperationException($"{type} is not a generic type");
if (type.IsGenericTypeDefinition)
throw new InvalidOperationException($"{type} is a generic type definition, it must be a constructed generic type");
var typeArgs = type.GetGenericArguments();
if (attr.TypeParameterPosition >= typeArgs.Length)
throw new ArgumentException($"Can't get type argument at position {attr.TypeParameterPosition}; {type} has only {typeArgs.Length} type arguments");
prop.PropertyName = typeArgs[attr.TypeParameterPosition].Name;
}
return prop;
}
}
Serialize with this resolver in your serialization settings:
var settings = new JsonSerializerSettings { ContractResolver = new GenericTypeNameContractResolver() };
string json = JsonConvert.SerializeObject(response, settings);
This will give the following output for Response<Foo>
{
"Foo": {
"Id": 0,
"Name": null
}
}
Here's a potentially easier way to achieve it. All you need to do is to have Response extend JObject, like this:
public class Response<T>: Newtonsoft.Json.Linq.JObject
{
private static string TypeName = (typeof(T)).Name;
private T _data;
public T Data {
get { return _data; }
set {
_data = value;
this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data);
}
}
}
If you do that, the following would work as you expect:
static void Main(string[] args)
{
var p1 = new Response<Int32>();
p1.Data = 5;
var p2 = new Response<string>();
p2.Data = "Message";
Console.Out.WriteLine("First: " + JsonConvert.SerializeObject(p1));
Console.Out.WriteLine("Second: " + JsonConvert.SerializeObject(p2));
}
Output:
First: {"Int32":5}
Second: {"String":"Message"}
In case you can't have Response<T> extend JObject, because you really need it to extend Response, you could have Response itself extend JObject, and then have Response<T> extend Response as before. It should work just the same.
#Thomas Levesque: OK. So let's say that you can't extend JObject in Response<T> because you need to extend a pre-existing Response class. Here's another way you could implement the same solution:
public class Payload<T> : Newtonsoft.Json.Linq.JObject {
private static string TypeName = (typeof(T)).Name;
private T _data;
public T Data {
get { return _data; }
set {
_data = value;
this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data);
}
}
}
//Response is a pre-existing class...
public class Response<T>: Response {
private Payload<T> Value;
public Response(T arg) {
Value = new Payload<T>() { Data = arg };
}
public static implicit operator JObject(Response<T> arg) {
return arg.Value;
}
public string Serialize() {
return Value.ToString();
}
}
So now there are the following options to Serialize the class:
static void Main(string[] args) {
var p1 = new Response<Int32>(5);
var p2 = new Response<string>("Message");
JObject p3 = new Response<double>(0.0);
var p4 = (JObject) new Response<DateTime>(DateTime.Now);
Console.Out.WriteLine(p1.Serialize());
Console.Out.WriteLine(p2.Serialize());
Console.Out.WriteLine(JsonConvert.SerializeObject(p3));
Console.Out.WriteLine(JsonConvert.SerializeObject(p4));
}
The Output will look something like this:
{"Int32":5}
{"String":"Message"}
{"Double":0.0}
{"DateTime":"2016-08-25T00:18:31.4882199-04:00"}

Duck Typing DynamicObject derivate

I wrote a class that allows a derivate to specify which of its properties can be lazy loaded. The code is:
public abstract class SelfHydratingEntity<T> : DynamicObject where T : class {
private readonly Dictionary<string, LoadableBackingField> fields;
public SelfHydratingEntity(T original) {
this.Original = original;
this.fields = this.GetBackingFields().ToDictionary(f => f.Name);
}
public T Original { get; private set; }
protected virtual IEnumerable<LoadableBackingField> GetBackingFields() {
yield break;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
result = field.GetValue();
return true;
} else {
var getter = PropertyAccessor.GetGetter(this.Original.GetType(), binder.Name);
result = getter(this.Original);
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
field.SetValue(value);
return true;
} else {
var setter = PropertyAccessor.GetSetter(this.Original.GetType(), binder.Name);
setter(this.Original, value);
return true;
}
}
}
And a derivate class:
public class SelfHydratingPerson : SelfHydratingEntity<IPerson> {
private readonly IDataRepository dataRepository;
public SelfHydratingDerivate(IDataRepository dataRepository, IPerson person)
: base(person) {
this.dataRepository = dataRepository
}
protected override IEnumerable<LoadableBackingField> GetBackingFields() {
yield return new LoadableBackingField("Address", () => this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
This works perfectly fine for getting and settings property values, but I get a either a RuntimeBinderException when I implicitly cast or an InvalidCastException with an explicitly cast SelfHydratingEntity back to T.
I know that you can override the DynamicObject.TryConvert method, but I'm wondering what exactly to put in this method. I've read a lot about duck typing today, and have tried out several libraries, but none of them work for this particular scenario. All of the libraries I've tried today generate a wrapper class using Reflection.Emit that makes calls to "get_" and "set_" methods and naturally use reflection to find these methods on the wrapped instance. SelfHydratingEntity of course doesn't have the "get_" and "set_" methods defined.
So, I'm wondering if this kind of thing is even possible. Is there any way to cast an instance of SelfHydratingEntity to T? I'm looking for something like this:
var original = GetOriginalPerson();
dynamic person = new SelfHydratingPerson(new DataRepository(), original);
string name = person.Name; // Gets property value on original
var address = person.Address; // Gets property value using LoadableBackingField registration
var iPerson = (IPerson)person;
- or -
var iPerson = DuckType.As<IPerson>(person);
Have you seen this Duck Typing project. It looks pretty good. I have just found a great example from Mauricio. It uses the Windsor Castle dynamic proxy to intercept method calls
Using the code from Mauricio the following code works like a dream
class Program
{
static void Main(string[] args)
{
dynamic person = new { Name = "Peter" };
var p = DuckType.As<IPerson>(person);
Console.WriteLine(p.Name);
}
}
public interface IPerson
{
string Name { get; set; }
}
public static class DuckType
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
impromptu-interface
https://github.com/ekonbenefits/impromptu-interface
Can static cast interfaces onto objects derived from DynamicObject.

Categories