简体   繁体   中英

Retrieve generic class from collection based on another type

I'm having trouble implementing a solution for encapsulating some CRUD logic and dto-entity mappings inside a separate service and really could use some help.

So let's say we have next interface that provides access endpoints to the data storage and we're having a separate client per entity

public interface IClient<T> where T : class
{
    T Get();
    void Set(T entity);
}

And then we have ClientService that serves as an aggregator to provide a single access point to all the existing clients

public class A { };
public class B { };

public class ClientService
{
    private IDictionary<Type, object> _clientDict;

    public ClientService(IClient<A> clientA, IClient<B> clientB)
    {
        _clientDict = new Dictionary<Type, object>
        {
            { typeof(A), clientA },
            { typeof(B), clientB }
        };
    }

    public T Get<T>() where T : class
    {
        var client = this.GetClient<T>();
        return client.Get();
    }

    public void Set<T>(T entity) where T : class
    {
        var client = this.GetClient<T>();
        client.Set(entity);
    }

    private IClient<T> GetClient<T>() where T : class
    {
        if (_clientDict.TryGetValue(typeof(T), out object client))
        {
            return (IClient<T>)client;
        }

        throw new ArgumentException();
    }
}

Everything is good so far.

Now the problem. I need to convert it to use DTO objects to work with IClient<T> while still accepting non-DTO models to the ClientService . Pseudocode:

public class A { };
public class A_DTO { };
public class B { };
public class B_DTO { };

public class ClientService
{
    private IDictionary<Type, object> _clientDict;

    public ClientService(IClient<A_DTO> clientA, IClient<B_DTO> clientB)
    {
        _clientDict = new Dictionary<Type, object>
        {
            { typeof(A), clientA },
            { typeof(B), clientB }
        };
    }

    public void Set<T>(T entity) where T : class
    {
        var dtoType = GetDTOType(T);
        var dtoEntity = _autoMapper.Map(entity, typeof(T), GetDTOType(T));

        var client = this.GetClient<T, dtoType>();
        client.Set(dtoEntity);
    }

    private IClient<DTO> GetClient<T, DTO>() where T : class
                                            where DTO: class
    {
        if (_clientDict.TryGetValue(typeof(T), out object client))
        {
            return (IClient<DTO>)client;
        }

        throw new ArgumentException();
    }
}

I do understand that since generics are being resolved during the compile time we're not able to make them dependant upon a variable but is there may be any other way in which we can get a client to work with, ie is there any way of how can we implement an IClient<T2> GetClient<T1, T2> where T1 is A and T2 is A_DTO ? Please note that end user does not have access to the DTO objects and so not able to provide it as a Generic type and everything needs to happen internally.

Is there any generic implementation or I'm just wasting time and should go with creating specific methods for every client implementation (ie Get<A>() , Get<B> etc.)?

You don't actually need to include your DTOs in your client service class. That will definitely get messy once your model starts growing. What you can do instead is use a tool like AutoMapper to convert the result from your service class to your dtos. It is pretty straightforward. You can have somethings like this:

public class A { };

public class DtoA { };

var source = ServiceClass.Get<A>();
var dto = Mapper.Map<A, ADto>(source);

See the link for more details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM