c# - Expression for read from property and write to private readonly field -
it long story ): have types this:
public class model { private readonly sometype _member; private readonly anothertype _member2; public model(sometype member, anothertype member2) { _member = member; _member2 = member2; } public sometype member { { return _member; } } public anothertype member2 { { return _member2; } } } i'm trying create expressions create instance of class, reads properties other objects (anon objects usually), , write values created instance's private fields -based on shown naming convention: prop has field name: _prop.
i mean want write below objects new instance of model:
var anon1 = new { member = "something" }; // expected: new model _member = "something" var anon2 = new { member2 = "something" }; // expected: new model _member2 = "something" var anon3 = new { member = "something", member2 = "something else" }; // expected: new model _member = "something" , _member2 = "something else" i have created below code, creates new instances, nothing fields. can me find did wrong please?
public class instancecreator { static public readonly func<fieldinfo, propertyinfo, bool> namingconvention; static instancecreator() { namingconvention = (f, p) => { var startswithunderscope = f.name.startswith("_"); var hassamename = p.name.equals(f.name.remove(0, 1), stringcomparison.ordinalignorecase); var hassametype = f.fieldtype == p.propertytype; return startswithunderscope && hassamename && hassametype && f.isinitonly && !p.canwrite; }; } private readonly type _type; private readonly func<dynamic, dynamic> _creator; public instancecreator(type type) { _type = type; var ctor = getctor(type); var propertytofieldwriters = makewriters(type); _creator = makecreator(type, ctor, propertytofieldwriters); } private expression getctor(type type) { if (type == typeof(string)) // ctor string return expression.lambda<func<dynamic>>( expression.constant(string.empty)); if (type.isvaluetype || // type has parameterless ctor type.getconstructor(type.emptytypes) != null) return expression.lambda<func<dynamic>>(expression.new(type)); var info = typeof(formatterservices).getmethod("getuninitializedobject"); var call = expression.call(info, expression.constant(type)); return call; //return expression.lambda<func<dynamic>>(call); } private ienumerable<propertytofieldmapper> makewriters(type type) { var properties = type.getproperties(bindingflags.instance | bindingflags.public); var fields = type.getfields(bindingflags.instance | bindingflags.nonpublic); var list = (from field in fields let property = properties.firstordefault(prop => namingconvention(field, prop)) property != null select new propertytofieldmapper(field, property)).tolist(); foreach (var item in list) { var sourceparameter = expression.parameter(type, "sourceparameter"); var propertygetter = expression.property(sourceparameter, item.property.name); var targetparameter = expression.parameter(type, "targetparameter"); var setterinfo = item.field.gettype().getmethod("setvalue", new[] { typeof(object), typeof(object) }); var settercall = expression.call(expression.constant(item.field), setterinfo, new expression[] { expression.convert(targetparameter,typeof(object)), expression.convert(propertygetter,typeof(object)) }); var call = expression.lambda(settercall, new[] { targetparameter, sourceparameter }); item.call = call; } return list; } private func<dynamic, dynamic> makecreator( type type, expression ctor, ienumerable<propertytofieldmapper> writers) { var list = new list<expression>(); // creating new target var targetvariable = expression.variable(type, "targetvariable"); list.add(expression.assign(targetvariable, expression.convert(ctor, type))); // find properties in incoming data var sourceparameter = expression.parameter(typeof(object), "sourceparameter"); var sourcetypevariable = expression.variable(typeof(type)); var sourcetypegetter = expression.call(sourceparameter, "gettype", type.emptytypes); list.add(expression.assign(sourcetypevariable, sourcetypegetter)); var sourcepropertiesvariable = expression.variable(typeof(propertyinfo[])); var sourcepropertiesgetter = expression.call(sourcetypevariable, "getproperties", type.emptytypes); list.add(expression.assign(sourcepropertiesvariable, sourcepropertiesgetter)); // itrate on writers , add call block foreach (var writer in writers) { var param = expression.parameter(typeof(propertyinfo)); var prop = expression.property(param, "name"); var eq = expression.equal(expression.constant(writer.property.name), prop); var = callany.call(sourcepropertiesvariable, expression.lambda(eq, param)); var predicate = expression.ifthen(any, expression.lambda(writer.call, new[] { targetvariable, sourceparameter })); list.add(predicate); } list.add(targetvariable); var block = expression.block(new[] { targetvariable, sourcetypevariable, sourcepropertiesvariable }, list); var lambda = expression.lambda<func<dynamic, dynamic>>( block, new[] { sourceparameter } ); return lambda.compile(); } public dynamic create(dynamic data) { return _creator.invoke(data); } private class propertytofieldmapper { private readonly fieldinfo _field; private readonly propertyinfo _property; public propertytofieldmapper(fieldinfo field, propertyinfo property) { _field = field; _property = property; } public fieldinfo field { { return _field; } } public propertyinfo property { { return _property; } } public expression call { get; set; } } } also, have callany class, created here.
public class callany { public static expression call(expression collection, expression predicate) { type ctype = getienumerableimpl(collection.type); collection = expression.convert(collection, ctype); type elemtype = ctype.getgenericarguments()[0]; type predtype = typeof(func<,>).makegenerictype(elemtype, typeof(bool)); // enumerable.any<t>(ienumerable<t>, func<t,bool>) var anymethod = (methodinfo) getgenericmethod(typeof(enumerable), "any", new[] { elemtype }, new[] { ctype, predtype }, bindingflags.static); return expression.call(anymethod, collection, predicate); } static methodbase getgenericmethod(type type, string name, type[] typeargs, type[] argtypes, bindingflags flags) { int typearity = typeargs.length; var methods = type.getmethods() .where(m => m.name == name) .where(m => m.getgenericarguments().length == typearity) .select(m => m.makegenericmethod(typeargs)); return type.defaultbinder.selectmethod(flags, methods.toarray(), argtypes, null); } static type getienumerableimpl(type type) { // ienumerable implementation. either type ienumerable<t> t, // or implements ienumerable<t> t. need find interface. if (isienumerable(type)) return type; type[] t = type.findinterfaces((m, o) => isienumerable(m), null); debug.assert(t.length == 1); return t[0]; } static bool isienumerable(type type) { return type.isgenerictype && type.getgenerictypedefinition() == typeof(ienumerable<>); } } and here usage:
var inst = new instancecreator(typeof (model)).create(new {mydata});
well, found problem. should use expandoobject instead of dynamic keyword. , pass logic idictionary<string, object>. here solution:
public class instancecreator { static public readonly func<fieldinfo, propertyinfo, bool> namingconvention; static instancecreator() { namingconvention = (f, p) => { var startswithunderscope = f.name.startswith("_"); var hassamename = p.name.equals(f.name.remove(0, 1), stringcomparison.ordinalignorecase); var hassametype = f.fieldtype == p.propertytype; return startswithunderscope && hassamename && hassametype && f.isinitonly && !p.canwrite; }; } private readonly type _type; private readonly func<idictionary<string, object>, dynamic> _creator; public instancecreator(type type) { _type = type; var ctor = getctor(type); var propertytofieldmappers = makemappers(type); _creator = makecreator(type, ctor, propertytofieldmappers); } private expression getctor(type type) { if (type == typeof(string)) // ctor string return expression.lambda<func<dynamic>>( expression.constant(string.empty)); if (type.isvaluetype || // type has parameterless ctor type.getconstructor(type.emptytypes) != null) return expression.lambda<func<dynamic>>(expression.new(type)); var info = typeof(formatterservices).getmethod("getuninitializedobject"); return expression.call(info, expression.constant(type)); } private ienumerable<propertytofieldmapper> makemappers(type type) { var properties = type.getproperties(bindingflags.instance | bindingflags.public); var fields = type.getfields(bindingflags.instance | bindingflags.nonpublic); var list = field in fields let property = properties.firstordefault(prop => namingconvention(field, prop)) property != null select new propertytofieldmapper(field, property); return list; } private func<idictionary<string, object>, dynamic> makecreator( type type, expression ctor, ienumerable<propertytofieldmapper> maps) { var list = new list<expression>(); var vlist = new list<parameterexpression>(); // creating new target var targetvariable = expression.variable(type, "targetvariable"); vlist.add(targetvariable); list.add(expression.assign(targetvariable, expression.convert(ctor, type))); // accessing source var sourcetype = typeof(idictionary<string, object>); var sourceparameter = expression.parameter(sourcetype, "sourceparameter"); // calling source containskey(string) method var containskeymethodinfo = sourcetype.getmethod("containskey", new[] { typeof(string) }); var accesssourceindexerprop = sourcetype.getproperty("item"); var accesssourceindexerinfo = accesssourceindexerprop.getgetmethod(); // itrate on writers , add call block var containskeymethodargument = expression.variable(typeof(string), "containskeymethodargument"); vlist.add(containskeymethodargument); foreach (var map in maps) { list.add(expression.assign(containskeymethodargument, expression.constant(map.property.name))); var containskeymethodcall = expression.call(sourceparameter, containskeymethodinfo, new expression[] { containskeymethodargument }); // creating writer var sourcevalue = expression.call(sourceparameter, accesssourceindexerinfo, new expression[] { containskeymethodargument }); var setterinfo = map.field.gettype().getmethod("setvalue", new[] { typeof(object), typeof(object) }); var settercall = expression.call(expression.constant(map.field), setterinfo, new expression[] { expression.convert(targetvariable,typeof(object)), expression.convert(sourcevalue,typeof(object)) }); list.add(expression.ifthen(containskeymethodcall, settercall)); } list.add(targetvariable); var block = expression.block(vlist, list); var lambda = expression.lambda<func<idictionary<string, object>, dynamic>>( block, new[] { sourceparameter } ); return lambda.compile(); } public dynamic create(idictionary<string, object> data) { return _creator.invoke(data); } private class propertytofieldmapper { private readonly fieldinfo _field; private readonly propertyinfo _property; public propertytofieldmapper(fieldinfo field, propertyinfo property) { _field = field; _property = property; } public fieldinfo field { { return _field; } } public propertyinfo property { { return _property; } } } }
Comments
Post a Comment