jeudi 7 septembre 2017

lambda using tuple via reflection

I have some code to sync entity framework POCOs between different database servers (src server is MSSQL, dst is MYSQL). It is written to work generically using [Key] attributes and the fact that the POCOs representing the data being synced know how to compare themselves. The code is currently as follows:

var srcdbset = setprop.GetValue(src, null); var dstdbset = setprop.GetValue(dst, null);
var tabletype = srcdbset.GetType().GetGenericArguments().First();
var keys = tabletype.GetProperties().Where(p => p.GetCustomAttributesData().FirstOrDefault(a => a.AttributeType.Name == "KeyAttribute") != null).ToList();

var param = Expression.Parameter(tabletype);  // TODO - support compound keys
var dt = typeof(Func<,>).MakeGenericType(tabletype, keys[0].PropertyType);
var df = Expression.Lambda(dt, Expression.Property(param, keys[0].Name), param).Compile();
var qtodict = typeof(Enumerable).GetMethods().Where(m => m.Name == "ToDictionary").First();
var gtodict = qtodict.MakeGenericMethod(new[] { tabletype, keys[0].PropertyType });
var srcdict = ((IDictionary)gtodict.Invoke(null, new object[] { srcdbset, df }));
var dstdict = ((IDictionary)gtodict.Invoke(null, new object[] { dstdbset, df }));
var qexcept = typeof(Enumerable).GetMethods().Where(m => m.Name == "Except").First();
var gexcept = qexcept.MakeGenericMethod(new[] { keys[0].PropertyType });
dynamic snotd = gexcept.Invoke(null, new object[] { srcdict.Keys, dstdict.Keys });
dynamic dnots = gexcept.Invoke(null, new object[] { dstdict.Keys, srcdict.Keys });

A little help - src is the source DbContext and dst is the destination DbContext and setprop is the PropertyInfo object of the DbSet being synced. This question really isn't so much about entity framework, but linq and reflection. As you can see the TODO comment says - "support compound keys" - the code above works fine for POCOs with just one key, but to support composite keys, the lambda expression needs to change from something like:

dbcontext.Accounts.ToDictionary(a=>a.AccountID);

to something like:

dbcontext.OntLocations.ToDictionary(l=>Tuple.Create(l.OntID, l.LocationID, l.Index);

The two linq expressions immediately above are obviously written in a non generic way to make things simpler to explain - my question is - how do I write a generic lambda that does the tuple create? If anyone could point me in the right direction on that, I think the rest of the code would work as is.

Also, you are probably thinking - why aren't they using transactional replication - long story short - unable to find a product that works reliably - if anyone knows of one that works well from MSSQL to MYSQL and doesn't require much/any downtime to the MSSQL server to install I'm all ears.





Aucun commentaire:

Enregistrer un commentaire