1

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?

Tyree Jackson
  • 2,588
  • 16
  • 22
Szer
  • 3,426
  • 3
  • 16
  • 36
  • I think the `GetEntityById` should not expose to client, it should be a private method and invoked by the wcf service. The relationship between entity and DTO is complex, if client call the gernic method directly, maybe it can not adapt the future changes – aspark Jul 30 '15 at 12:28

1 Answers1

1

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.

Tyree Jackson
  • 2,588
  • 16
  • 22
  • Excellent idea. Thank you, sir! One question: you mention "If you are ok with using a static resolver". Whats the problem with static resolver? – Szer Jul 31 '15 at 06:40
  • There's nothing wrong with it per se if it gets the job done. If your were using a DI strategy, then directly accessing a static resolver would be in opposition to that strategy. However, it would be trivial to add an injectable adapter to decouple that concern. So no real worries there. – Tyree Jackson Jul 31 '15 at 08:57