I'm trying to get every child of Commands in multiple assembly's to store them in a List but in order to do that i need to create an instance of that child in order to store it but so i am trying to use Activator.CreateInstance the goal is to have a ctor public for outside usage and a ctor for the Activator so it can create instances to store but, problem is that Activator just can't find the ctor for some reason, i even tagged the ctor as public but no luck
public abstract class Command
{
public static List<Command> List { get; set; }
public static Dictionary<Type, int> Lookup { get; set; }
public Command(int id, FieldInfo[] field)
{
Id = id;
Fields = field;
}
public Command()
{
Command command = List[Lookup[GetType()]];
Id = command.Id;
Fields = command.Fields;
}
public static void Initialize()
{
Lookup = new Dictionary<Type, int>();
List = new List<Command>();
foreach (Type type in
AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(Command))))
{
Command command = (Command)Activator.CreateInstance(type, List.Count, type.GetFields());
Lookup.Add(type, command.Id);
List.Add(command);
}
}
}
public class PlayerMove : Command
{
}
[TestClass()]
public class PacketTests
{
[TestMethod()]
public void PackTest()
{
Command.Initialize();
Packet packet = new Packet();
var cmd = new PlayerMove()
{
};
cmd.Send(Method.Unreliable);
var g = Command.List;
}
What am i doing wrong ?
You are trying to construct an instance of PlayerMove. This only has a default constructor, i.e. PlayerMove(), which will call the base constructor Command(). Therefore your call to Activator.CreateInstance should look like
Command command = (Command)Activator.CreateInstance(type);
Alternatively you should add an additional constructor to the PlayerMove class:
public class PlayerMove : Command
{
public PlayerMove(int id, FieldInfo[] field) : base(id, field){}
public PlayerMove() : base(){}
}
But I think that probably what you actually want is something like this:
public abstract class Command
{
public static List<Command> List { get; private set; }
public static Dictionary<Type, int> Lookup { get; private set; }
public int Id { get; }
public FieldInfo[] Fields { get; }
protected Command(int id, FieldInfo[] field)
{
Id = id;
Fields = field;
}
protected Command()
{
Command command = List[Lookup[GetType()]];
Id = command.Id;
Fields = command.Fields;
}
public static void Initialize()
{
Lookup = new Dictionary<Type, int>();
List = new List<Command>();
foreach (Type type in
AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(Command))))
{
Command command = (Command) Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.NonPublic, null,
new object[] {List.Count, type.GetFields()}, null);
Lookup.Add(type, command.Id);
List.Add(command);
}
}
}
public class PlayerMove : Command
{
private PlayerMove(int id, FieldInfo[] field) : base(id, field)
{
}
public PlayerMove()
{
}
}
This adds a private constructor to the PlayerMove command that is called through reflection and populates the Id and Fields properties on the base class, and a paramaterless constructor that can then be used by other clients of the class.
Related
I have big model, that aggragates data for buisness entity.
class BigObject
{
TypeA DataA { get;set; }
TypeB DataB { get;set; }
TypeC DataC { get;set; }
}
and have service, which fill fields of model from differents sources. Some data depends from another data
class DataService
{
public BigObject GetModel()
{
var model = new BigObject();
model.DataA = sourceServiceA.GetData();
model.DataB = sourceServiceB.GetData(model.DataA.Id);
model.DataC = sourceServiceC.GetData();
}
}
In method GetModel() I need to configure, which fields should be filled, which should not. For example, I want to fill DataA property, but don't want fill others. First idea is pass in method object BigObjectFilter
public BigObject GetModel(BigObjectFilter filter)
class BigObjectFilter
{
bool FillDataA { get; set; }
bool FillDataB { get; set; }
bool FillDataC { get; set; }
}
and initialize this object in DataService clients.
In GetObject method I was going to add conditions like
if (filter.FillDataA)
{
model.DataA = sourceServiceA.GetData();
}
if (filter.FillDataC)
{
model.DataC = sourceServiceC.GetData();
}
I see, that this solution looks like bad practice. I would like to improve this construction. How can i improve it? I can't see, how to use builder pattern in this case, because i have requeired and optional data, one depends on the other.
For the sake of simplicity let's assume that TypeA, TypeB and TypeC are int?.
We can define a command class for the BigObject with the following constructors:
class BigObjectCommand
{
private readonly Func<BigObjectFilter, bool> canExecute;
private readonly Action<BigObject>? executeWithoutParam;
private readonly Action<int, BigObject>? executeWithParam;
private readonly Expression<Func<BigObject, int?>>? dependsOn;
public BigObjectCommand(Func<BigObjectFilter, bool> canExecute, Action<BigObject> execute)
{
this.canExecute = canExecute;
this.executeWithoutParam = execute;
}
public BigObjectCommand(Func<BigObjectFilter, bool> canExecute, Action<int, BigObject> execute, Expression<Func<BigObject, int?>> dependsOn)
{
this.canExecute = canExecute;
this.executeWithParam = execute;
this.dependsOn = dependsOn;
}
}
The first constructor will be used to cover the DataA and DataC properties' initialization
The second constructor will be used to cover the initialization of DataB property
Now, we can define an Evaluate method to implement the core logic
public void Evaluate(BigObjectFilter filter, BigObject context)
{
if (!canExecute(filter))
return; //or throw exception
if (executeWithoutParam is not null)
{
executeWithoutParam(context);
return;
}
var input = dependsOn!.Compile()(context);
if (!input.HasValue)
return; //or throw exception
executeWithParam!(input.Value, context);
}
If the condition fails we don't do the assignment
If the assignment does not require any input then we simply execute it
If the assignment depends on an input then we check whether it is populated or not and depending on the result we may or may not execute the assignment
With these in our hand the GetModel can be implemented like this:
private readonly List<BigObjectCommand> Commands;
public DataService()
{
Commands = new()
{
new (filter => filter.FillDataA, (m) => m.DataA = sourceServiceA.GetData()),
new (filter => filter.FillDataB, (i, m) => m.DataB = sourceServiceB.GetData(i), m => m.DataA),
new (filter => filter.FillDataC, (m) => m.DataC = sourceServiceC.GetData()),
};
}
public BigObject GetModel(BigObjectFilter filter)
{
var model = new BigObject();
foreach (var command in Commands)
{
command.Evaluate(filter, model);
}
return model;
}
This solution is far from perfect I just wanted to share with you the basic idea how to apply the Command pattern for your problem.
Here you can a find a working example. By changing the FillDataXYZ values you can see how the Evaluate works in practice.
It looks like you have at least two choices here:
use some collection which stores value to handle
an approach inspired by Chain-of-responsibility pattern
Let's start from collection which stores value to handle
At first, we need our class with properties to be filled:
public class BigObject
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
Then this is our class which will handle all your properties:
public class BigObjectHandler
{
Dictionary<string, Action> _handlerByproperty = new ();
BigObject _bigObject;
public BigObjectHandler(BigObject bigObject)
{
_bigObject = bigObject;
_handlerByproperty.Add("A", GetDataA);
_handlerByproperty.Add("B", GetDataB);
_handlerByproperty.Add("C", GetDataC);
}
public void Handle(string propertyName) =>
_handlerByproperty[propertyName].Invoke();
private void GetDataA()
{
_bigObject.A = 1; // sourceServiceA.GetData();
}
private void GetDataB()
{
_bigObject.B = 1; // sourceServiceA.GetData();
}
private void GetDataC()
{
_bigObject.C = 1; // sourceServiceA.GetData();
}
}
And then you can call the above code like this:
IEnumerable<string> propertiesToFill = new List<string> { "A", "B" };
BigObject bigObject = new ();
BigObjectHandler bigObjectMapHandler = new (bigObject);
foreach (var propertyToFill in propertiesToFill)
{
bigObjectMapHandler.Handle(propertyToFill);
}
OUTPUT:
A = 1
B = 1
Chain-of-responsibility pattern
If you have many if else statements, then you can try to use "Chain-of-responsibility pattern". As wiki says:
the chain-of-responsibility pattern is a behavioral design pattern
consisting of a source of command objects and a series of processing
objects. Each processing object contains logic that defines the
types of command objects that it can handle; the rest are passed to
the next processing object in the chain. A mechanism also exists for
adding new processing objects to the end of this chain
However, we will not stop execution if some of condition is met. Let me show an example.
At first, we need some abstraction of handler:
public abstract class BigObjectHandler
{
private BigObjectHandler _nextBigObjectHandler;
public void SetSuccessor(BigObjectHandler bigObjectHandler)
{
_nextBigObjectHandler = bigObjectHandler;
}
public virtual BigObject Execute(BigObject bigObject,
BigObjectFilter parameter)
{
if (_nextBigObjectHandler != null)
return _nextBigObjectHandler.Execute(bigObject, parameter);
return bigObject;
}
}
Then we need concrete implemenatation of these handlers for your properties. This properties will be filled
by your sourceServiceX.GetData():
public class BigObjectAHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillA)
{
bigObject.A = 1; // sourceServiceA.GetData();
}
return base.Execute(bigObject, filter);
}
}
And:
public class BigObjectBHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillB)
{
bigObject.B = 2; // sourceServiceB.GetData();
}
return base.Execute(bigObject, filter);
}
}
And:
public class BigObjectCHandler : BigObjectHandler
{
public override BigObject Execute(BigObject bigObject, BigObjectFilter filter)
{
if (filter.FillC)
{
bigObject.C = 3; // sourceServiceC.GetData();
}
return base.Execute(bigObject, filter);
}
}
And these are object with data:
public class BigObject
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
And some filter which will contain settings of what property should be filled:
public class BigObjectFilter
{
public bool FillA { get; set; } = true;
public bool FillB { get; set; }
public bool FillC { get; set; }
}
And then we can call the above code like this:
BigObjectHandler chain = new BigObjectAHandler();
BigObjectHandler objectBHandler = new BigObjectBHandler();
BigObjectHandler objectCHandler = new BigObjectCHandler();
chain.SetSuccessor(objectBHandler);
objectBHandler.SetSuccessor(objectCHandler);
BigObjectFilter bigObjectFilter = new BigObjectFilter();
bigObjectFilter.FillA = true;
BigObject vehicle = chain.Execute(new BigObject(), bigObjectFilter); // A = 1
It can be seen after code execution that onle property A is handled. Output is:
A = 1
B = 1
I hope it becomes clear what I mean. I have multiple static class full of options:
static class Thing1
{
public const string Name = "Thing 1";
// More const fields here.
}
static class Thing2
{
public const string Name = "Thing 2";
// More const fields here.
}
Now I want to use those options to create a class which includes the contents of one of these classes.
public void Create<T>()
{
var foo = new Foo(T.Name);
foo.Prop = T.Something;
if (T.HasValue)
foo.Add(T.Value);
}
But of course this doesn't work. I would use interfaces, but static classes can't implement interfaces.
Is there any way to make this work elegantly? Making Thing1 and Thing2 singletons would work, but that isn't a very nice solution.
I could create a struct and put the objects into another static class, but I was wondering whether you could do something like the above.
Well, you can create an interface and make your classes non-static and inherit from this interface:
public class Thing1 : IThing
{
public string Name { get; } = "Thing 1";
// More const fields here.
}
public class Thing2 : IThing
{
public string Name { get; } = "Thing 2";
// More fields here.
}
interface IThing
{
string Name { get; }
}
And then use it for your method together with type parameter constraint:
public void Create<T>(T t) where T : IThing
{
// Now compiler knows that `T` has all properties from `IThing`
var foo = new Foo(t.Name);
foo.Prop = t.Something;
if (t.HasValue)
foo.Add(t.Value);
}
You can try Reflection: scan assemblies for static classes, obtain public const string fields with their values from them and materialize them as a Dictionary<T>
using System.Linq;
using System.Reflection;
...
// Key : type + field name, say Tuple.Create(typeof(Thing1), "Name")
// Value : corresponding value, say "Thing 1";
static Dictionary<Tuple<Type, string>, string> s_Dictionary = AppDomain
.CurrentDomain
.GetAssemblies() // I've taken all assemblies; you may want to add Where here
.SelectMany(asm => asm.GetTypes())
.Where(t => t.IsAbstract && t.IsSealed) // All static types, you may want to add Where
.SelectMany(t => t
.GetFields() // All constant string fields
.Where(f => f.FieldType == typeof(string))
.Where(f => f.IsPublic && f.IsStatic)
.Where(f => f.IsLiteral && !f.IsInitOnly) // constants only
.Select(f => new {
key = Tuple.Create(t, f.Name),
value = f.GetValue(null)
}))
.ToDictionary(item => item.key, item => item.value?.ToString());
If you want to scan not all loaded but just one (executing) assembly
static Dictionary<Tuple<Type, string>, string> s_Dictionary = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsAbstract && t.IsSealed)
...
Now you can wrap the dictionary, say
public static string ReadConstant<T>(string name = null) {
if (string.IsNullOrEmpty(name))
name = "Name";
if (s_Dictionary.TryGetValue(Tuple.Create(typeof(T), name), out string value))
return value;
else
return null; // Or throw exception
}
Usage
string name1 = ReadConstant<Thing1>();
You could use non static classes and add those to a dictionary having a Type key. But you would have to use read-only properties.
public interface IConstants
{
string Name { get; }
double InitialHealth { get; }
public int? MaxTries { get; }
}
public class Thing1 : IConstants
{
public string Name => "Thing 1";
public double InitialHealth => 100.0;
public int? MaxTries => null;
}
public class Thing2 : IConstants
{
public string Name => "Thing 2";
public double InitialHealth => 80.0;
public int? MaxTries => 10;
}
Initialize the dictionary:
public static readonly Dictionary<Type, IConstants> Constants =
new Dictionary<Type, IConstants> {
[typeof(Thing1)] = new Thing1(),
[typeof(Thing2)] = new Thing2(),
};
The Create function:
public void Create<T>()
{
Type key = typeof(T);
var foo = new Foo(key.Name);
IConstants constants = Constants[key];
foo.InitialHealth = constants.InitialHealth;
if (constants.MaxTries is int maxTries) { // Only true if MaxTries.HasValue.
// Converts to int at the same time.
foo.Add(maxTries);
}
}
Instead of a static classes with constants. You can create a class with properties and static instances with the desired values.
public class Thing
{
private Thing(string name, string something, bool hasValue, string value)
{
Name = name;
Something = something;
HasValue = hasValue;
Value = value;
}
public string Name { get; }
public string Something{ get; }
public bool HasValue { get; }
public string Value{ get; }
public static Thing Thing1 { get; } = new Thing("Thing1", "Something1", true, "Value1");
public static Thing Thing2 { get; } = new Thing("Thing2", "Something2", false, null);
}
And then your method would just take that class.
public void Create(Thing t)
{
var foo = new Foo(t.Name);
foo.Prop = t.Something;
if (t.HasValue)
foo.Add(t.Value);
}
Then you'd call it with either
Create(Thing.Thing1);
or
Create(Thing.Thing2);
After some reflection I came up with another solution. Why select the constants by type? It is much easier if we use the same type to store the different sets of constants.
public class Constants
{
public string Name { get; set; }
public double Health { get; set; }
public int? MaxTries { get; set; }
}
We then identify the sets through an enum:
public enum SetType
{
Set1, // Please use speaking names in a real implementation!
Set2,
Set3
}
We define the values of the constants while creating the dictionary of constants sets:
public static readonly Dictionary<SetType, Constants> ConstantSets =
new Dictionary<SetType, Constants> {
[SetType.Set1] = new Constants { Name = "Set 1", Health = 100, MaxTries = null },
[SetType.Set2] = new Constants { Name = "Set 2", Health = 80, MaxTries = 5 },
...
};
The Create method becomes
public void Create(SetType set)
{
var constants = ConstantSets[set];
var foo = new Foo(constants.Name) {
Health = constants.Health
};
if (constants.MaxTries is int maxTries) {
foo.Add(maxTries);
}
}
No generics, no reflection, no fancy stuff required.
I want to convert an object A to object B. The classes A and B have the same properties, just the names are changed.
I use this method:
/// <summary>
internal static T objectMapper<T>(object objectSource, T objectTarget)
{
dynamic o = objectSource;
Type typeA = objectSource.GetType();
Type typeB = objectTarget.GetType();
IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());
dynamic s;
ArrayList listArray = new ArrayList();
foreach (var prop in propsA)
{
s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
listArray.Add(s);
}
int i = 0;
foreach (var prop in propsB)
{
prop.SetValue(objectTarget, listArray[i], null);
i++;
}
return objectTarget;
}
How can I edit properties of objectB in the foreach loop? I want to use a generic method for different objects.
This solution provides both your reflection-way and an alternative way by defining and implementing a copy method CopyFrom. To reduce code you could make the interface a base-class so you don't need to implement CopyFrom in the sub-classes....
public interface MyInterface
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void CopyFrom(MyInterface obj);
}
public class A: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public class B: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public static class CopyUtils
{
public static void Copy(MyInterface src, MyInterface dst)
{
var props = typeof(MyInterface).GetProperties();
foreach(var prop in props)
{
prop.SetValue(dst, prop.GetValue(src, null), null);
}
}
}
I feel there might be a deeper architecture issue here. I'm failing to imagine why would you want to "copy" the values of the properties from one object of a class to another of a different class with the same property names.
If you're trying to "shape" the object maybe just passing an interface will do the work
Anyhow, see if this helps:
public static class ObjectMorpher
{
public class InvalidMorphException : Exception
{
}
[AttributeUsage(AttributeTargets.Property)]
public class IgnoredOnMorphAttribute : Attribute
{
}
public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
where TargetType : class
{
if (source == null || dest == null)
throw new ArgumentNullException();
foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
{
var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
//check property exists
if (destProp == null)
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
//check value type is assignable
if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
destProp.SetValue(dest, sourceProp.GetValue(source));
}
return dest;
}
}
Usage example:
var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);
Optionally you can set a property match for the case when properties doesn't have the exact same name.
Also notice the use of the IgnoredOnMorph attribute to mark properties as not morph-able (like calculated properties)
You might find automapper of use here (see https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).
You would need to create a line for each object mapping in a startup file to set it up but if the properties are the same this would be as simple as:
mapper.CreateMap<ClassA, ClassB>().ReverseMap();
And then a single line to resolve the mapping when needed
mapper.Map(objectOfClassA, new ClassB());
I have a class which implements some interface:
public interface IDb {}
public class DbModel : IDb {}
After this I use Dapper Extensions to insert the object into the DB. This code works well:
var obj = new DbModel();
sqlConnection.Insert(obj);
But when I try insert an instance of this class, casting on the corresponding interface, it gives an Exception:
IDb obj = new DbModel();
sqlConnection.Insert(obj); // exception here
System.ArgumentException: 'No columns were mapped.'
Reason why it not work:
Proceeding from the fact that, DapperExtensions.Insert method is generic, DapperExstensions can't find corresponding map for IDb
Solution:
Create mapper class for IDb and register DbModel properties
public sealed class IDbMapper : ClassMapper<IDb>
{
public IDbMapper()
{
base.Table("TableName");
Map(m => new DbModel().Title);
// and such mapping for other properties
}
}
After this it will be work:
IDb obj = new DbModel();
sqlConnection.Insert(obj);
But here is a problem, when we have many implementations of IDb
interface, in IDbMapper config we can't map columns to corresponding
tables here:
base.Table("TableName");
Map(m => new DbModel().Title);
Because we don't know type of object instance when we do mapping.
Edit:
After some debuging I notice that, in every Insert method call, dapper do mapping, and construct corresponding ClassMapper<> class. We can use this weirdness.
For this we should create SharedState and store in it instance type before calling Insert method.
public static class DapperExstensionsExstensions
{
public static Type SharedState_ModelInstanceType { get; set; }
...
}
IDb obj = new DbModel();
DapperExstensionsExstensions.SharedState_ModelInstanceType = obj.GetType();
sqlConnection.Insert(obj);
After this we can access this property when Dapper will do mapping
public sealed class IDbMapper : ClassMapper<IDb>
{
public IDbMapper()
{
// here we can retrieve Type of model instance and do mapping using reflection
DapperExstensionsExstensions.SharedState_ModelInstanceType
}
}
Whole code snippet:
public interface IDb { }
[MapConfig("TableName", "Schema")]
public class DbTemp : IDb
{
public string Title { get; set; }
}
public class MapConfigAttribute : Attribute
{
public MapConfigAttribute(string name, string schema)
{
Name = name;
Schema = schema;
}
public string Name { get; }
public string Schema { get; }
}
public sealed class DbMapper : ClassMapper<IDb>
{
public DbMapper()
{
DapperExstensionsExstensions.CorrespondingTypeMapper<IDb>((tableName, sechemaName, exprs) =>
{
Table(tableName);
Schema(SchemaName);
return exprs.Select(Map);
});
}
}
public static class DapperExstensionsExstensions
{
private static readonly object _LOCK = new object();
public static Type SharedState_ModelInstanceType { get; set; }
public static List<PropertyMap> CorrespondingTypeMapper<T>(Func<string, string, IEnumerable<Expression<Func<T, object>>>, IEnumerable<PropertyMap>> callback)
{
var tableNameAttribute = (MapConfigAttribute)SharedState_ModelInstanceType.GetCustomAttribute(typeof(MapConfigAttribute));
var tableName = tableNameAttribute.Name;
var schemaName = tableNameAttribute.Schema;
var result = callback(tableName, schemaName, new GetPropertyExpressions<T>(SharedState_ModelInstanceType));
Monitor.Exit(_LOCK);
return result.ToList();
}
public static object Insert<TInput>(this IDbConnection connection, TInput entity) where TInput : class
{
Monitor.Enter(_LOCK);
SharedState_ModelInstanceType = entity.GetType();
return DapperExtensions.DapperExtensions.Insert(connection, entity);
}
}
public class GetPropertyExpressions<TInput> : IEnumerable<Expression<Func<TInput, object>>>
{
private readonly Type _instanceType;
public GetPropertyExpressions(Type instanceType)
{
_instanceType = instanceType;
}
public IEnumerator<Expression<Func<TInput, object>>> GetEnumerator()
{
return _instanceType
.GetProperties()
.Select(p => new GetPropertyExpression<TInput>(_instanceType, p.Name).Content()).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class GetPropertyExpression<TInput> : IContent<Expression<Func<TInput, object>>>
{
private readonly Type _instanceType;
private readonly string _propertyName;
public GetPropertyExpression(Type instanceType, string propertyName)
{
_instanceType = instanceType;
_propertyName = propertyName;
}
public Expression<Func<TInput, object>> Content()
{
// Expression<Func<IDb, object>> :: model => (object)(new DbModel().Title)
var newInstance = Expression.New(_instanceType);
var parameter = Expression.Parameter(typeof(TInput), "model");
var getPropertyExpression = Expression.Property(newInstance, _propertyName);
var convertedProperty = Expression.Convert(getPropertyExpression, typeof(object));
var lambdaExpression = Expression.Lambda(convertedProperty, parameter);
return (Expression<Func<TInput, object>>)lambdaExpression;
}
}
It works for me
IDb obj = new DbModel();
sqlConnection.Insert(obj);
I'm trying to use AutoMapper to map classes like this:
class FooDTO
{
public int X { get; set; }
public EmbeddedDTO Embedded { get; set; }
public class EmbeddedDTO
{
public BarDTO Y { get; set; }
public BazDTO Z { get; set; }
}
}
To classes like this:
class Foo
{
public int X { get; set; }
public Bar Y { get; set; }
public Baz Z { get; set; }
}
(FooDTO is a HAL resource)
I know I can do it by creating the map explicitly like this:
Mapper.CreateMap<FooDTO, Foo>()
.ForMember(f => f.Y, c => c.MapFrom(f => f.Embedded.Y))
.ForMember(f => f.Z, c => c.MapFrom(f => f.Embedded.Z));
Or even with a trick like this:
Mapper.CreateMap<FooDTO, Foo>()
.AfterMap((source, dest) => Mapper.Map(source.Embedded, dest));
But the problem is that I will have many similar HAL resources to map, and I'd rather not have to configure each one separately. I actually have a generic object model that looks like this:
class HalResource
{
[JsonProperty("_links")]
public IDictionary<string, HalLink> Links { get; set; }
}
class HalResource<TEmbedded> : HalResource
{
[JsonProperty("_embedded")]
public TEmbedded Embedded { get; set; }
}
class HalLink
{
[JsonProperty("href")]
public string Href { get; set; }
}
With this model, the FooDTO class is actually declared like this
class FooDTO : HalResource<FooDTO.EmbeddedDTO>
{
public int X { get; set; }
public class EmbeddedDTO
{
public int Y { get; set; }
public int Z { get; set; }
}
}
Is there a way to configure the mapping globally for all classes that inherit HalResource<TEmbedded>, so that the properties of the DTO's Embedded property are mapped directly to the target object? I tried to do it with a custom IObjectMapper, but it proved more challenging than I expected...
If your use case is as limited as presented in the question, that is:
A one-way mapping from HalResource derived instances to straight POCOS (vs bidirectional mapping)
Mapping of properties of the same name and type
The exact embedded structure you presented here
than it may make sense to setup a specific mapping yourself that takes into account this structure. This is something I tend to do if I have a very narrowly defined need for mapping with some clear mapping conventions (instead of relying on a generic mapper such as AutoMapper). For this purpose I have some building blocks that I tend to reuse in different contexts. I whipped together a mapper that applies to the problem you described from these building blocks, as shown below:
public class Mapper
{
private const BindingFlags DestConstructorFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags DestFlags = BindingFlags.Instance | BindingFlags.Public;
private const BindingFlags SrcFlags = BindingFlags.Instance | BindingFlags.Public;
private static readonly object[] NoArgs = new object[0];
private static readonly Type GenericEmbeddedSourceType = typeof(HalResource<>);
private readonly Dictionary<Type, Func<object, object>> _oneWayMap = new Dictionary<Type, Func<object, object>>();
public void CreateMap<TDestination, TSource>()
where TDestination : class
where TSource : HalResource
{
CreateMap(typeof(TDestination), typeof(TSource));
}
public void CreateMap(Type destType, Type srcType)
{
_oneWayMap[srcType] = InternalCreateMapper(destType, srcType);
}
public object Map<TSource>(TSource toMap) where TSource : HalResource
{
var mapper = default(Func<object, object>);
if (!_oneWayMap.TryGetValue(typeof(TSource), out mapper))
throw new KeyNotFoundException(string.Format("No mapping for {0} is defined.", typeof(TSource)));
return mapper(toMap);
}
public TDestination Map<TDestination, TSource>(TSource toMap)
where TDestination : class
where TSource : HalResource
{
var converted = Map(toMap);
if (converted != null && !typeof(TDestination).IsAssignableFrom(converted.GetType()))
throw new InvalidOperationException(string.Format("No mapping from type {0} to type {1} has been configured.", typeof(TSource), typeof(TDestination)));
return (TDestination)converted;
}
public void Clear()
{
_oneWayMap.Clear();
}
private static Func<object, object> InternalCreateMapper(Type destType, Type srcType)
{
// Destination specific constructor + setter map.
var destConstructor = BuildConstructor(destType.GetConstructor(DestConstructorFlags, null, Type.EmptyTypes, null));
var destSetters = destType
.GetProperties(DestFlags)
.Where(p => p.CanWrite)
.ToDictionary(k => k.Name, v => Tuple.Create(v.PropertyType, BuildSetter(v)));
// Source specific getter maps
var srcPrimPropGetters = CreateGetters(srcType);
var srcEmbeddedGetter = default(Func<object, object>);
var srcEmbeddedPropGetters = default(IDictionary<string, Tuple<Type, Func<object, object>>>);
var baseType = srcType.BaseType;
while (baseType != null && baseType != typeof(object))
{
if (baseType.IsGenericType && GenericEmbeddedSourceType.IsAssignableFrom(baseType.GetGenericTypeDefinition()))
{
var genericParamType = baseType.GetGenericArguments()[0];
if (srcPrimPropGetters.Any(g => g.Value.Item1.Equals(genericParamType)))
{
var entry = srcPrimPropGetters.First(g => g.Value.Item1.Equals(genericParamType));
srcPrimPropGetters.Remove(entry.Key);
srcEmbeddedGetter = entry.Value.Item2;
srcEmbeddedPropGetters = CreateGetters(entry.Value.Item1);
break;
}
}
baseType = baseType.BaseType;
}
// Build mapper delegate function.
return (src) =>
{
var result = destConstructor(NoArgs);
var srcEmbedded = srcEmbeddedGetter != null ? srcEmbeddedGetter(src) : null;
foreach (var setter in destSetters)
{
var getter = default(Tuple<Type, Func<object, object>>);
if (srcPrimPropGetters.TryGetValue(setter.Key, out getter) && setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result, getter.Item2(src));
else if (srcEmbeddedPropGetters.TryGetValue(setter.Key, out getter) && setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result, getter.Item2(srcEmbedded));
}
return result;
};
}
private static IDictionary<string, Tuple<Type, Func<object, object>>> CreateGetters(Type srcType)
{
return srcType
.GetProperties(SrcFlags)
.Where(p => p.CanRead)
.ToDictionary(k => k.Name, v => Tuple.Create(v.PropertyType, BuildGetter(v)));
}
private static Func<object[], object> BuildConstructor(ConstructorInfo constructorInfo)
{
var param = Expression.Parameter(typeof(object[]), "args");
var argsExp = constructorInfo.GetParameters()
.Select((p, i) => Expression.Convert(Expression.ArrayIndex(param, Expression.Constant(i)), p.ParameterType))
.ToArray();
return Expression.Lambda<Func<object[], object>>(Expression.New(constructorInfo, argsExp), param).Compile();
}
private static Func<object, object> BuildGetter(PropertyInfo propertyInfo)
{
var instance = Expression.Parameter(typeof(object), "instance");
var instanceCast = propertyInfo.DeclaringType.IsValueType
? Expression.Convert(instance, propertyInfo.DeclaringType)
: Expression.TypeAs(instance, propertyInfo.DeclaringType);
var propertyCast = Expression.TypeAs(Expression.Property(instanceCast, propertyInfo), typeof(object));
return Expression.Lambda<Func<object, object>>(propertyCast, instance).Compile();
}
private static Action<object, object> BuildSetter(PropertyInfo propertyInfo)
{
var setMethodInfo = propertyInfo.GetSetMethod(true);
var instance = Expression.Parameter(typeof(object), "instance");
var value = Expression.Parameter(typeof(object), "value");
var instanceCast = propertyInfo.DeclaringType.IsValueType
? Expression.Convert(instance, propertyInfo.DeclaringType)
: Expression.TypeAs(instance, propertyInfo.DeclaringType);
var call = Expression.Call(instanceCast, setMethodInfo, Expression.Convert(value, propertyInfo.PropertyType));
return Expression.Lambda<Action<object, object>>(call, instance, value).Compile();
}
}
Some optimizations can be performed, but performance is likely sufficient for most problems. This can then be used like:
public abstract class HalResource
{
public IDictionary<string, HalLink> Links { get; set; }
}
public abstract class HalResource<TEmbedded> : HalResource
{
public TEmbedded Embedded { get; set; }
}
public class HalLink
{
public string Href { get; set; }
}
public class FooDTO : HalResource<FooDTO.EmbeddedDTO>
{
public int X { get; set; }
public class EmbeddedDTO
{
public int Y { get; set; }
public int Z { get; set; }
}
}
public class MyMappedFoo
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
class Program
{
public static void Main(params string[] args)
{
// Configure mapper manually
var mapper = new Mapper();
mapper.CreateMap<MyMappedFoo, FooDTO>();
var myDTO = new FooDTO
{
X = 10,
Embedded = new FooDTO.EmbeddedDTO { Y = 5, Z = 9 }
};
var mappedFoo = mapper.Map<MyMappedFoo, FooDTO>(myDTO);
Console.WriteLine("X = {0}, Y = {1}, Z = {2}", mappedFoo.X, mappedFoo.Y, mappedFoo.Z);
Console.WriteLine("Done");
Console.ReadLine();
}
}
If your source and destination types can be discovered by convention, you can go a step further and have a builder that encodes these conventions populate the map as in the example below (again not the most optimal implementation, but there to illustrate the point):
public static class ByConventionMapBuilder
{
public static Func<IEnumerable<Type>> DestinationTypesProvider = DefaultDestTypesProvider;
public static Func<IEnumerable<Type>> SourceTypesProvider = DefaultSourceTypesProvider;
public static Func<Type, Type, bool> TypeMatcher = DefaultTypeMatcher;
public static Mapper Build()
{
var mapper = new Mapper();
var sourceTypes = SourceTypesProvider().ToList();
var destTypes = DestinationTypesProvider();
foreach (var destCandidateType in destTypes)
{
var match = sourceTypes.FirstOrDefault(t => TypeMatcher(t, destCandidateType));
if (match != null)
{
mapper.CreateMap(destCandidateType, match);
sourceTypes.Remove(match);
}
}
return mapper;
}
public static IEnumerable<Type> TypesFromAssembliesWhere(Func<IEnumerable<Assembly>> assembliesProvider, Predicate<Type> matches)
{
foreach (var a in assembliesProvider())
{
foreach (var t in a.GetTypes())
{
if (matches(t))
yield return t;
}
}
}
private static IEnumerable<Type> DefaultDestTypesProvider()
{
return TypesFromAssembliesWhere(
() => new[] { Assembly.GetExecutingAssembly() },
t => t.IsClass && !t.IsAbstract && !t.Name.EndsWith("DTO"));
}
private static IEnumerable<Type> DefaultSourceTypesProvider()
{
return TypesFromAssembliesWhere(
() => new[] { Assembly.GetExecutingAssembly() },
t => typeof(HalResource).IsAssignableFrom(t) && !t.IsAbstract && t.Name.EndsWith("DTO"));
}
private static bool DefaultTypeMatcher(Type srcType, Type destType)
{
var stn = srcType.Name;
return (stn.Length > 3 && stn.EndsWith("DTO") && destType.Name.EndsWith(stn.Substring(0, stn.Length - 3)));
}
}
class Program
{
public static void Main(params string[] args)
{
// Configure mapper by type scanning & convention matching
var mapper = ByConventionMapBuilder.Build();
var myDTO = new FooDTO
{
X = 10,
Embedded = new FooDTO.EmbeddedDTO { Y = 5, Z = 9 }
};
var mappedFoo = mapper.Map<MyMappedFoo, FooDTO>(myDTO);
Console.WriteLine("X = {0}, Y = {1}, Z = {2}", mappedFoo.X, mappedFoo.Y, mappedFoo.Z);
Console.WriteLine("Done");
Console.ReadLine();
}
}
If you have other reasons to want to hang on to AutoMapper, I suggest creating a similar map builder that encodes both the type matching and the embedded property mapping.