I am moving from inheritance to compositon, as you can see from here https://stackoverflow.com/questions/29653153/composition-migrating-from-inheritance
Now i have got it all working, but simple injector wants me to manually register each interface with each type being passed in. Here is the striped down code.
I have IBaseEntityService, which BaseEntityService implements, like so
public interface IEntityBaseService<T> where T : class, IEntityBase
{
IDataContext Context { get; }
Task<ICollection<T>> GetAllAsync();
Task<T> GetAsync(long id);
}
public class EntityBaseService<T> : IEntityBaseService<T>
where T : class, IEntityBase
{
protected IDataContext _context;
public IDataContext Context
{
get
{
return _context;
}
}
public EntityBaseService(IDataContext context)
{
_context = context;
}
public async Task<ICollection<T>> GetAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
public Task<T> GetAsync(long id)
{
return _context.Set<T>().Where(e => e.Id == id).FirstOrDefaultAsync();
}
}
Now i have a IValidationService and ValidationService implements that, like so
public interface IValidationService<T>
where T : class, IEntityBase
{
Task<ValidationResult> ValidateAsync(T entity);
Task<int> AddAsync(T entity);
Task<int> UpdateAsync(T entity);
}
public class ValidationService<T> : IValidationService<T>
where T : class, IEntityBase
{
private IEntityBaseService<T> _service;
private IValidator<T> _validator = null;
public IDataContext Context
{
get
{
return _service.Context;
}
}
public ValidationService(IEntityBaseService<T> service, IValidator<T> validator)
{
_service = service;
_validator = validator;
}
public Task<ValidationResult> ValidateAsync(T entity)
{
if (_validator == null) throw new MissingFieldException("Validator does not exist for class " + entity.GetType().ToString() + ". override method if no validation needed");
return _validator.ValidateAsync(entity);
}
public async Task<int> AddAsync(T entity)
{
var results = await ValidateAsync(entity);
if (!results.IsValid)
{
throw new ValidationException(results.Errors);
}
return await _service.AddAsync(entity);
}
public async Task<int> UpdateAsync(T entity)
{
var results = await ValidateAsync(entity);
if (!results.IsValid)
{
throw new ValidationException(results.Errors);
}
return await _service.UpdateAsync(entity);
}
}
Now i can use services, and pass in each interface. So i have a IContentService and ContentService which implements that. This service uses both IEntityBaseService and IValidationService
public interface IContentService
{
Task<Content> GetAsync(long id);
Task<int> AddAsync(Content entity);
Task<int> UpdateAsync(Content entity);
}
public class ContentService : IContentService
{
private IEntityBaseService<Content> _service;
private IValidationService<Content> _validation;
public ContentService(IEntityBaseService<Content> service, IValidationService<Content> validation)
{
_service = service;
_validation = validation;
}
public async Task<Content> GetAsync(long id)
{
var content = await _service.Context.Contents
.Where(e => e.Id == id)
.WhereNotDeleted()
.FirstOrDefaultAsync();
if (content != null)
{
content.Attachments = await _service.Context.Attachments
.Where(e => e.ContentId == id)
.WhereNotDeleted()
.ToListAsync();
}
return content;
}
public Task<int> AddAsync(Content entity)
{
return _validation.AddAsync(entity);
}
public Task<int> UpdateAsync(Content entity)
{
return _validation.UpdateAsync(entity);
}
}
Now in my config file, i have this, which works, but if i have another service, and i have about 20 of them, i do not want to type this out for each one.
container.Register<IEntityBaseService<Content>, EntityBaseService<Content>>();
container.Register<IValidationService<Content>, ValidationService<Content>>();
container.Register<IValidator<Content>, ContentValidator>();
container.Register<IContentService, ContentService>();
So i looked online and i think i can use the RegisterManyForOpenGeneric method instead of registering IEntityBaseService and IValidatioNService, but i cannot get it too work. I have wrote
container.RegisterManyForOpenGeneric(typeof(IEntityBaseService<>), typeof(IEntityBaseService<>).Assembly);
container.RegisterManyForOpenGeneric(typeof(IValidationService<>), typeof(IValidationService<>).Assembly);
And i get an error saying
The constructor of type ContentService contains the parameter of type IEntityBaseService with name 'service' that is not registered. Please ensure IEntityBaseService is registered in the container, or change the constructor of ContentService.
Maybe i have set up my entities incorrectly, so just encase, here are the basic entities for this example
public interface IEntityBase
{
long Id { get; set; }
}
public abstract class EntityBase : IEntityBase
{
public long Id { get; set; }
}
public class Content : EntityBase, IAudit
{
public short Position { get; set; }
public Content()
{
Position = 1;
}
}
Any help much appreciated on both simple injector and the code in general
You're using the wrong registration method.
RegisterManyForOpenGeneric
exists to support the following scenario:-
container.Register<IValidate<Customer>, CustomerValidator>();
container.Register<IValidate<Employee>, EmployeeValidator>();
container.Register<IValidate<Order>, OrderValidator>();
container.Register<IValidate<Product>, ProductValidator>();
// can replace the above with the following:-
container.RegisterManyForOpenGeneric(
typeof(IValidate<>),
typeof(IValidate<>).Assembly);
By default RegisterManyForOpenGeneric searches the supplied assembly for all types that implement the [specified open generic] interface and registers each type by their specific (closed generic) interface. [Emphasis mine]
Note how the concrete types aren't themselves generic (i.e. CustomerValidator
implements the closed generic interface IValidate<Customer>
). This works fine for your ContentValidator
but is no good for open generic classes implementing open generic interfaces like your EntityBaseService<Content>
and ValidationService<Content>
.
You're looking for:-
container.RegisterOpenGeneric(
typeof(IEntityBaseService<>),
typeof(EntityBaseService<>));