Md. Sabbir Ahamed
Md. Sabbir Ahamed

Reputation: 485

How to Convert a generic type list of data to another generic type list of data

I am trying to write a Generic Base Service Class where after receiving the fist generic list of data as the actual type of Db Model Entity need a conversion to a new Generic View Model type of data.

I have tried list.ConvertAll() but always getting a build error for ConvertAll() method.

I also tried list.Cast<TVm>().ToList() this solve the build error but getting the run time error.

Here are my code snips of all classes and interfaces. Any help or suggestion is appreciated.

Entity Class

public abstract class Entity
{
    [Key]
    [Index("IX_Id", 1, IsUnique = true)]
    public string Id { get; set; }

    [DataType(DataType.DateTime)]
    public DateTime Created { get; set; }

    public string CreatedBy { get; set; }

    [DataType(DataType.DateTime)]
    public DateTime Modified { get; set; }

    public string ModifiedBy { get; set; }

    [DefaultValue(true)]
    public bool Active { get; set; }
}

BaseViewModel Class

public abstract class BaseViewModel<T> where T: Entity
{        

    protected BaseViewModel() { }

    protected BaseViewModel(T model)
    {
        Id = model.Id;
        Created = model.Created;
        CreatedBy = model.CreatedBy;
        Modified = model.Modified;
        ModifiedBy = model.ModifiedBy;
        Active = model.Active;
    }

    public string Id { get; set; }
    public DateTime Created { get; set; }
    public string CreatedBy { get; set; }
    public DateTime Modified { get; set; }
    public string ModifiedBy { get; set; }
    public bool Active { get; set; }
}

IBaseService interface

public interface IBaseService<T, TVm> where T : Entity where TVm : BaseViewModel<T>
{
    List<TVm> GetAll();
}

BaseService Class

public abstract class BaseService<TEntity, TVm> : IBaseService<TEntity, TVm> where TEntity: Entity where TVm : BaseViewModel<TEntity>
{
    protected  IBaseRepository<TEntity> Repository;

    protected BaseService(IBaseRepository<TEntity> repository)
    {
        Repository = repository;
    }

    public virtual  List<TVm> GetAll()
    {
        List<TVm> entities;
        try
        {
            List<TEntity> list = Repository.GetAll().ToList();
            entities =  list.Cast<TVm>().ToList(); //runtime error
            entities = list.ConvertAll(x => new TVm(x)); //build error
            entities = list.ConvertAll(new Converter<TEntity, TVm>(TEntity)); //build error
        }
        catch (Exception exception)
        {                
            throw new Exception(exception.Message);
        }

        return entities;
    }

}

Upvotes: 6

Views: 724

Answers (2)

Adil Mammadov
Adil Mammadov

Reputation: 8686

I think the best option you tried is entities = list.ConvertAll(x => new TVm(x));. The reason it fails compilation is that, in C# inherited classes doesn't necessarily need to have same constructor as base class. Instead you should initialize TVm inside BaseService. Additionally, your try catch block does nothing more than losing some data and affecting StackTrace, so you better remove it. Below code should work for you.

public abstract class BaseService<TEntity, TVm> 
    : IBaseService<TEntity, TVm> 
        where TEntity: Entity 
        where TVm : BaseViewModel<TEntity>, new()
{
    protected  IBaseRepository<TEntity> Repository;

    protected BaseService(IBaseRepository<TEntity> repository)
    {
        Repository = repository;
    }

    private TVm ConvertToViewModel(TEntity model)
    {
        return new TVm
        {
            Id = model.Id,
            Created = model.Created,
            CreatedBy = model.CreatedBy,
            Modified = model.Modified,
            ModifiedBy = model.ModifiedBy,
            Active = model.Active,
        };
    }

    public virtual  List<TVm> GetAll()
    {
        List<TEntity> list = Repository.GetAll().ToList();
        List<TVm> entities = list.ConvertAll(x => ConvertToViewModel(x));
        return entities;
    }
}

Upvotes: 0

Koneke
Koneke

Reputation: 178

To create an instance of a generic type, you need the new()-constraint on it. However, that does not allow you to pass any parameters to it. You could try either

  1. Using Activator to create an instance, like this

    entities = list.ConvertAll(x => (TVm)Activator.CreateInstance(typeof(TVm), x));

or

  1. Add the new()-constraint to TVm in the BaseService class signature, and add a method on the classes you pass to it as TVm that basically does what the current constructor does, but in a method, and call that after creating the new object.

Upvotes: 2

Related Questions