Reputation: 485
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
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
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
Using Activator to create an instance, like this
entities = list.ConvertAll(x => (TVm)Activator.CreateInstance(typeof(TVm), x));
or
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