I am working on a WCF service and I have run into a bit of a snag mapping my entities to my DTO. Consider the following
namespace Foo.Entities
{
public class Order : IOrder
{
public string Name { get;set; }
public string Address { get;set; }
public IList<ILocation> Locations { get;set; }
}
}
namespace Foo.DTO
{
[DataContract]
public class Order
{
[DataMember]
public string Name { get;set; }
[DataMember]
public string Address { get;set; }
[DataMember]
public List<Location> Locations { get;set; }
}
}
This is all very straightforward: DTO.Order is what I am returning from my endpoint and Entities.Order is what I am using internally (I am using DI / IOC) for business logic, data operations, etc. Since my business layer returns types from the Entities namespace, but the endpoint returns types from the DTO namespace I wrote a small mapping method that will take one type and map it to another type like so:
public TTarget MapObject<TSource, TTarget>(TSource source, TTarget target)
where TSource : class
where TTarget : class
{
foreach (var prop in source.GetType().GetProperties())
{
var targetProp = target.GetType().GetProperty(prop.Name, BindingFlags.Public | BindingFlags.Instance);
if(targetProp == null || !targetProp.CanWrite) continue;
if (prop.PropertyType.GetGenericTypeDefinition() == typeof (IList<>))
{
??
}
else{ targetProp.SetValue(target, prop.GetValue(source)); }
}
return target;
}
I then call this method like so:
factory.MapObject(Entities.DealerOrder, new GTO.DealerOrder())
where Entities.DealerOrder represents an instantiated object that contains data.
Everything works fine until I get to the property of type IList and I am at a loss at how to convert the IList to List. I know what needs to happen but all of the documentation I have read thus far hasn't pointed me in the right direction.
The pseudo is
if (prop.PropertyType.GetGenericTypeDefinition() == typeof (IList<>))
{
var lst = new List<type of targetProp>()
foreach(var val in prop.GetValue())
{
var item = new Location() (I have to figure out this initialization based on the List type of targetProp. In this case it would be List<Location>)
var retval = MapObject(val, item);
lst.Add(retval);
}
targetProp.SetValue(target, lst);
}
I am not sure if what I want to do is even possible. I know that Generics and Reflection don't mix well so if there is a solution it might be overly complex for what I am really trying to accomplish. If worse comes to worse I can put a static method on each of my DTO's that will accept the source type as a parameter and return an instance of the DTO, but I want to avoid having to manually map the fields from the Entity to the DTO if at all possible.
Any help is greatly appreciated.
You can use targetProp.GetGenericArguments()[0]; to get the type of item you want to map your collection content to.
You can use Activator.CreateInstance to create List<T> with T known at runtime at not at compile time.
You can use Activator.CreateInstance to create instance of the type you want to map to.
You can't rely on type inference when calling MapObject anymore. You need to create proper generic method via reflection here too, and call it.
You can't simply call Add on the list, because you don't know what kind of list it is. You can cast it to ICollection and call Add on it instead.
Can't you just use something like AutoMapper? Those are problems people already solved, why don't you use their work?
Related
I've got two objects which (Domain and Data) which in this case have the same property (let's presume Name). I've got an Action<DomItem> which I would like to cast to Action<DataItem>.
public class DomItem {
public string Name { get; set; }
}
public class DataItem {
public string Name { get; set; }
}
public class Program {
public Program() {
Action<DomItem> domAction = new Action<DomItem>(x=>x.Name = "Test");
// Something Casted To Action<DataItem>(x=>x.Name = "Test");
}
}
Of course this is just a basic example. It's by design that I can NOT use a common interface. I do not care about the DataItem might not be having the same property.
I've been looking into Expressions and several other solutions but I just can't figure out how to create the Cast (or get the "x=>x.Name =..." part from the method).
Any help would be really appreciated!
You can't directly or indirectly cast a Action<DomItem> to an Action<DataItem>, but you could wrap the action with a converter that converts the input from a DataItem to a DomItem and runs the original action on the copy:
public Action<DataItem> Convert(Action<DomItem> action)
{
return new Action<DataItem>(o => action(Map(o)));
}
public DomItem Map(DataItem dataItem)
{
return new DomItem{Name = dataItem.Name};
}
The obvious downside is that the action will be applied to a copy of the original object and not the original object itself. Without knowing exactly what the action is I don't know of a way to "cast" the action without a common base type.
If you're unfamiliar with the Entity Framework, it generates a class that looks like
public partial class contextontext : DbContext
{
public virtual DbSet<foo> foo { get; set; }
public virtual DbSet<bar> bar { get; set; }
//Etc, there could be lots of these DbSet properties
}
I'm trying to build a list of types that there are DbSet<T> collections for, but I'm not sure how to check for a generic type. I got it to work using PropertyType.ToString().StartsWith("System.Data.Entity.DbSet`1") but that seems like a messy and unnecessarily complicated way to do it. This is the full LINQ I use to get the properties.
foreach (var property in context.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(p => p.PropertyType.ToString().StartsWith("System.Data.Entity.DbSet`1")))
{
Type type = property.PropertyType.GenericTypeArguments[0];
//other stuff...
}
I poked around in the property object, but didn't find any leads. There's a Type in there, but that's always the type specific implementation of the generic DbSet (as it should be). How would I check to see if it's the generic collection, regardless of what type it is?
I think you want simply:
var propertyType = property.PropertyType;
if (propertyType.IsGenericType
&& propertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
{
// ...
}
I see now your Where far to the right in your code. That would be:
.Where(p => p.PropertyType.IsGenericType
&& p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
The method GetGenericTypeDefinition goes from the concrete constructed ("closed") generic type (e.g. DbSet<foo>), to the definition of the type (here DbSet<TEntity>).
I have two custom types Customer and Employee which implement the interface ITablefy. This interface has only one method, GetPropertyList which returns a list of strings of the property names of the object that implements it. I have a web service which looks like:
public string ReturnPropertyNames(ITablefy i)
{
List<string> propList = new List<string>();
TableFactory factory = new TableFactory();
ITablefy table = factory.CreateTable(i);
propList = table.GetPropertyList(table);
return propList[1];
}
so in this example the Factory creates a concrete type that implements ITablefy
I realized when I had a problem when both of my classes Customer and Employee implemented their GetPropertyList methods exactly the same:
//property list is a private member variable in each class
public List<string> GetPropertyList(ITablefy i)
{
TableFactory factory = new TableFactory();
ITablefy table = factory.CreateTable(i);
foreach (var propInfo in table.GetType().GetProperties())
{
propertyList.Add(propInfo.Name);
}
return propertyList;
}
Rather than copy and paste that code I'm looking for a better solution to what I have currently. If I only want certain types to use the GetPropertyList method how can I control that without having to copy and paste this same code? Harcoding the type to create in each class doesn't seem like a good solution to me. Employee and Customer don't logically make sense to use inheritance either. What's a proper solution for something like this?
factory:
public class TableFactory
{
public ITablefy CreateTable(ITablefy i)
{
if (i is Employee)
{
return new Employee();
}
else if (i is Customer)
{
return new Customer();
}
else
{
return null;
}
}
}
public static List<string> GetPropertyNames(this Object o)
{
List<string> names = new List<string>
foreach (PropertyInfo prop in o.GetType().GetProperties())
names.Add(prop.Name);
return names;
}
Now you can implement ITablefy in terms of any object.GetPropertyNames() using the extension method above.
There are a few questions that comes to my mind:
If It's so easy to do generically, why are you even using the interface?
Shouldn't you be checking properties for public accessors?
Shouldn't your interface be returning a more general type like IEnumerable<string> or ICollection<string>?
Wouldn't the interface be better designed to filter out property names that you don't want? That way you could assume all public properties are part of the set except those that aren't.
You make the interface be something like:
public interface IPropertyInfoFilterProvider {
public Func<PropertyInfo, bool> PropertyInfoSkipFilter { get; set; }
}
or
public interface IPropertyNameFilterProvider {
public Func<string, bool> PropertyNameSkipFilter { get; set; }
}
and then you can initialize the default to (prop) => false.
so now you can harvest the property names automagically and in one place and let implementations determine what gets taken and what doesn't and your harvesting code could use that filter in a linq where clause.
You could make it an extension method on ITablefy.
Or a static method on ITablefy
I want to add an item to a Generic list using reflection. In the method "DoSomething", I am trying to finish the following line,
pi.PropertyType.GetMethod("Add").Invoke(??????)
but I am getting different kinds of error.
Below is my complete code
public class MyBaseClass
{
public int VechicleId { get; set; }
}
public class Car:MyBaseClass
{
public string Make { get; set; }
}
public class Bike : MyBaseClass
{
public int CC { get; set; }
}
public class Main
{
public string AgencyName { get; set; }
public MyBaseCollection<Car> lstCar {get;set;}
public void DoSomething()
{
PropertyInfo[] p =this.GetType().GetProperties();
foreach (PropertyInfo pi in p)
{
if (pi.PropertyType.Name.Contains("MyBaseCollection"))
{
//Cln contains List<Car>
IEnumerable<MyBaseClass> cln = pi.GetValue(this, null) as IEnumerable<MyBaseClass>;
**//Now using reflection i want to add a new car to my object this.MyBaseCollection**
pi.PropertyType.GetMethod("Add").Invoke(??????)
}
}
}
}
Any ideas / suggestion ?
I think you want:
// Cast to IEnumerable<MyBaseClass> isn't helping you, so why bother?
object cln = pi.GetValue(this, null);
// Create myBaseClassInstance.
// (How will you do this though, if you don't know the element-type?)
MyBaseClass myBaseClassInstance = ...
// Invoke Add method on 'cln', passing 'myBaseClassInstance' as the only argument.
pi.PropertyType.GetMethod("Add").Invoke(cln, new[] { myBaseClassInstance } );
Since you don't know what the element-type of the collection is going to be (could be Car, Bike, Cycle etc.) you're going to find it hard to find a useful cast. For example, although you say the collection will definitely implement IList<SomeMyBaseClassSubType>, that isn't all that helpful since IList<T> isn't covariant. Of course, casting to IEnumerable<MyBaseClass> should succeed, but that won't help you since it doesn't support mutations. On the other hand, if your collection-type implemented the non-generic IList or ICollection types, casting to those might come in handy.
But if you're sure that the collection will implement IList<Car> (i.e. you know the element-type of the collection beforehand), things are easier:
// A much more useful cast.
IList<Car> cln = (IList<Car>)pi.GetValue(this, null);
// Create car.
Car car = ...
// The cast helped!
cln.Add(car);
As an alternative... Just don't; consider the non-generic IList interface:
IList list = (IList) {... get value ...}
list.Add(newItem);
While it isn't obligatory for all generic collections to implement IList, they pretty much all do, since it underpins such a lot of core framework code.
start with typeof<List<>>.GetMethods, you do not invoke a method of the property, but a method of the type of the property
Could you just avoid reflection all together and use:
List<MyBaseClass> lstCar { get; set; }
lstCar.Add((MyBaseClass)new Car());
You could also consider using an interface or abstract methods...
I fear this is going to be a big setup for a simple question. As to complexity of the answer, I fear what I might be getting into...
I am building an application that will be used to help transform data from a source database with one table structure to a target database with a different structure. The target database will contain data already, and thus the process must be able to maintain ID-based relationships from the source when inserting to the target, where the newly-inserted items will get new IDs. Assume that each source table will be transformable to a single target table.
Minimal code, with necessary class/interface structure:
public interface IDataSetStorable { }
public class InMemoryDataSet : List<IDataSetStorable>
{
public AbstractDataEntity FindEntity(string id, Type type)
{
// The question will be about this method
return new object() as AbstractDataEntity;
}
}
public class EntityList<T> : Dictionary<string, T>, IDataSetStorable where T : AbstractDataEntity
{
public void AddEntity(T entity)
{
this.Add(entity.ID, entity);
}
}
public abstract class AbstractDataEntity
{
public string ID { get; set; }
}
public abstract class DataItem<S, T> : AbstractDataEntity { }
// There will be a set of these three classes per source DB table
public class SourceType { }
public class TargetType { }
public class TransformationType : DataItem<SourceType, TargetType> { }
InMemoryDataSet holds the tables, represented by instances of (for example) EntityList<TransformationType>. There will be a TransformationType for each mapping of SourceType to TargetType, where each of those is likely to be a class from a DataContext. There will be one per source DB table, though many of those tables may map to a single target DB table.
The use of IDataSetStorable as a marker interface allows for the storage of EntityList<>s with many different subtypes within an instance of InMemoryDataSet.
During the transformation of any item from the source DB, it can only be inserted into the target DB if we know the appropriate target-DB IDs for its foreign keys. To do this the code will find all its dependencies from the source DB and transform them BEFORE attempting to transform the item under consideration. Recursively, this should ensure that the first things inserted into the target DB have no dependencies, get their new IDs, and can then be looked up when inserting things that depend on them.
An instance of InMemoryDataSet will provide the lookup facility, which should be passed an ID (from the source DB) and a parameter of type Type, representing the TransformationType which deals with transforming the type of item being looked up.
Example of that: Table1 has two fields, id and table2_id, the latter referencing Table2, and its field id. The lookup call would be (kinda pseudocode-y):
var entity = myDataSet.FindEntity([table1.table2_id], typeof(Table2TransformationType));
Then entity should be of type Table2TransformationType (inheriting eventually from AbstractDataEntity), and would represent the row from Table2 with ID matching that passed to the method.
And finally, to the question:
In the FindEntity() method, how can I find if there is an EntityList<whatever the passed type was> present? My thought was to use something like:
foreach (var entityList in this)
{
// work out if entityList is an EntityList<passed-in type>;
}
Simple question! But I don't know how I can do this last part :(
You need to check:
If the Type for the current item entityList represents a generic type
If that generic type represents EntityList<>
If the generic argument to that type is of the passed in type
Try this:
if (entityList.GetType().IsGenericType &&
entityList.GetType().GetGenericTypeDefinition() == typeof(EntityList<>) &&
entityList.GetType().GetGenericArguments()[0] == type)
{
...
}
Edit: Was getting the generic arguments off of the wrong type. Fixed.
OK, managed to make this work using a bit of Reflection. Kirk Woll got me started looking in the right places, though in the end the solution hasn't used his suggestions. There is an additional method, public T RetrieveEntity(string id), in the EntityList<T> class in order to make it easier to get a single item out of the Dictionary by key when using Type.GetMethod():
public class EntityList<T> : Dictionary<string, T>, IDataSetStorable where T : AbstractDataEntity
{
public void AddEntity(T entity)
{
this.Add(entity.ID, entity);
}
public T RetrieveEntity(string id)
{
return this[id];
}
}
Then we have the guts of the FindEntity(string id, Type type) method:
public class InMemoryDataSet : List<IDataSetStorable>
{
public AbstractDataEntity FindEntity(string id, Type type)
{
// Make an instance of the passed-in type so that invoking
// TryGetValue will throw an exception if operating on an
// EntityList which is not of the correct type.
var sample = type.GetConstructor(new Type[]{}).Invoke(new object[]{});
foreach (var entityList in this)
{
try
{
// This doesn't manage to set sample to the found entity...
bool idFound = (bool)entityList.GetType().GetMethod("TryGetValue").Invoke(entityList, new object[] { id, sample });
if (idFound)
{
// So we dig it out here with the method added to EntityList<>
sample = entityList.GetType().GetMethod("RetrieveEntity").Invoke(entityList, new object[] { id });
return (AbstractDataEntity)sample;
}
}
catch (Exception ex)
{
// Likely some kind of casting exception
}
}
return null;
}
}
At the point of calling FindEntity() we knew what the desired type was, and so casting the AbstractDataEntity that it returns is trivial.
Use Linq:
Dictionary<string, Type> a = new Dictionary<string, Type>();
var allOfMyType = a.Where(x=> (x.Value.Name == "MyType"));