I have a WCF service which works with SQL through EntityFramework via net.tcp
Its allowed to clients to query items by Id from Db.
I have a plenty of methods which looks like this:
public SiteDTO GetSiteById(long siteId)
{
using (var context = new MyDbContext())
{
var site = context.Site.Find(siteId);
return AutoMapper.Mapper.Map<SiteDTO>(site);
}
}
so I decided to make The One Method to rule them all:
public TDTO GetEntityById<TDTO, TSet>(object id)
where TDTO : class
where TSet : class
{
using (var context = new MyDbContext())
{
var entity = context.Set<TSet>().Find(id);
if (entity == null)
return default(TDTO);
return AutoMapper.Mapper.Map<TSet, TDTO>(entity);
}
}
but the problem is that the client which should use it know nothing about TSet
type (its a database type and clients only works with DTOs), so this method cannot be called that way. I need to make it like that:
public TDTO GetEntityById<TDTO>(object id)
where TDTO : class
{
using (var context = new MyDbContext())
{
//Something like this and a lot of reflection...
Type setType = Resolver(typeof(TDTO));
//I know this won't work. Just to show my intentions
var entity = context.Set<setType>().Find(id);
if (entity == null)
return default(TDTO);
return AutoMapper.Mapper.Map<setType, TDTO>(entity);
}
}
I know how to solve the problem robust way - make Dictionary<Type,Type>
register it one time and use it.
Question: Is there more elegance way (maybe with AutoMapper methods) to do it?
If you are ok with using a static resolver, then the following should work:
public static class DTOResolver
{
public static void RegisterSetForDTO<TSet, TDTO>()
where TDTO : class
where TSet : class
{
DTOResolver<TDTO>.SetType = typeof(TSet);
DTOResolver<TDTO>.SetMapper = new DTOResolver<TDTO>.Mapper<TSet>();
}
}
public static class DTOResolver<TDTO> where TDTO : class
{
public abstract class Mapper
{
public abstract TDTO Map(Object entity);
}
public class Mapper<TSet> : Mapper
{
public override TDTO Map(Object entity)
{
return AutoMapper.Mapper.Map<TSet, TDTO>((TSet) entity);
}
}
public static Type SetType { get; set; }
public static Mapper SetMapper { get; set; }
}
Assuming DTOs and Sets like this:
public class DTO1 {}
public class Set1 {}
public class DTO2 {}
public class Set2 {}
Register your mappings like this:
static void Setup()
{
DTOResolver.RegisterSetForDTO<Set1, DTO1>();
DTOResolver.RegisterSetForDTO<Set2, DTO2>();
}
And modify your GetEntityById
like this:
public TDTO GetEntityById<TDTO>(object id)
where TDTO : class
{
using (var context = new MyDbContext())
{
var entity = context.Set(DTOResolver<TDTO>.SetType).Find(id);
if (entity == null)
return default(TDTO);
return DTOResolver<TDTO>.SetMapper.Map(entity);
}
}
The reason this works is that the DTOResolver<TDTO>
defines a new static boundary in memory specific to a single TDTO
type enabling us to register a single Type
for the Set to use for that TDTO
and a single Mapper
subclass to instantiate a singleton for that is typed to a specific TSet
.