-
-
Save CallumVass/8300400 to your computer and use it in GitHub Desktop.
public class SalesAppContextProvider : EFContextProvider<SalesAppContext> | |
{ | |
private readonly ServiceFactory _serviceFactory; | |
public SalesAppContextProvider(ServiceFactory serviceFactory) | |
{ | |
_serviceFactory = serviceFactory; | |
} | |
protected override bool BeforeSaveEntity(EntityInfo entityInfo) | |
{ | |
var entityType = entityInfo.Entity.GetType(); | |
var service = _serviceFactory.GetServiceFor(entityType); | |
switch (entityInfo.EntityState) | |
{ | |
case EntityState.Added: | |
return service.BeforeSaveEntityAdded(entityInfo.Entity); | |
case EntityState.Modified: | |
return service.BeforeSaveEntityModified(entityInfo.Entity); | |
case EntityState.Deleted: | |
return service.BeforeSaveEntityDeleted(entityInfo.Entity); | |
default: | |
return true; | |
} | |
} | |
protected override void AfterSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap, List<KeyMapping> keyMappings) | |
{ | |
foreach (var map in saveMap) | |
{ | |
var entityType = map.Key; | |
var service = _serviceFactory.GetServiceFor(entityType); | |
foreach (var entityInfo in map.Value) | |
{ | |
switch (entityInfo.EntityState) | |
{ | |
case EntityState.Added: | |
service.AfterSaveEntityAdded(entityInfo.Entity); | |
break; | |
case EntityState.Modified: | |
service.AfterSaveEntityModified(entityInfo.Entity); | |
break; | |
case EntityState.Deleted: | |
service.AfterSaveEntityDeleted(entityInfo.Entity); | |
break; | |
} | |
} | |
} | |
base.AfterSaveEntities(saveMap, keyMappings); | |
} | |
} | |
public class CustomerService : IService | |
{ | |
private readonly IMailService _mailService; | |
public CustomerService(IMailServce mailService) { | |
_mailService = mailService; | |
} | |
public bool BeforeSaveEntityAdded(object entity) | |
{ | |
return true; | |
} | |
public bool BeforeSaveEntityModified(object entity) | |
{ | |
return true; | |
} | |
public bool BeforeSaveEntityDeleted(object entity) | |
{ | |
return true; | |
} | |
public void AfterSaveEntityAdded(object entity) | |
{ | |
var customer = entity as Customer; | |
_mailService.NewCustomerNotification(customer); | |
} | |
public void AfterSaveEntityModified(object entity) | |
{ | |
} | |
public void AfterSaveEntityDeleted(object entity) | |
{ | |
} | |
} | |
public class ServiceFactory | |
{ | |
private readonly IMailService _mailService; | |
public ServiceFactory(IMailService mailService) { | |
_mailService = mailService; | |
} | |
private readonly Dictionary<Type, int> _typeDictionary = new Dictionary<Type, int> | |
{ | |
{ typeof(Collection), 0 }, | |
{ typeof(Customer), 1 } | |
}; | |
public IService GetServiceFor(Type entityType) | |
{ | |
switch (_typeDictionary[entityType]) | |
{ | |
case 0: | |
return new CollectionService(); | |
case 1: | |
return new CustomerService(_mailService); | |
default: | |
throw new ArgumentException("entityType"); | |
} | |
} | |
} |
I guess the bit that felt "hacky" to me was the type dictionary, and having to add a new one in each time I add a new service for an Entity. Also, the new'ing up of the concrete implementation rather than in the IoC container, which might be better perhaps for handling any dependencies the services may have? But yes, overall I'm quite pleased with it. Thanks for your comment, it's really appreciated! I will take those points on board and re-factor a bit more! :)
Yes, for sure you'd inject rather than new up the factory. You could get fancy and use MEF to have type-specific services self-register. But you're doing the most important thing which is factoring the business logic so that it isn't all lumped in a gigantic before/after interceptor and your factoring is both rational and flexible.
I've done such factoring myself in DocCode but I like your approach better and will probably steal it in a future DocCode update (I'll credit you and this gist).
I think it's a fine and flexible start. Doesn't look hacky to me.
A few observations:
no need to call the base method of any of the injectors as they are empty; could just do this
return service.BeforeSaveOperation(entityInfo.Entity);
I prefer
beforeSaveEntities
becauseEntityType
in the change-setSaveMap
itselfI would provide the
SaveMap
itself as a parameter to your save operationsI foresee useful extensions for
SaveChanges
endpoints if I had them (see "Named Saves"Rename
loadServiceFor
asgetServiceFor
so I don't have a heart attack thinking that you'll be re-fetching and re-initializing the type-specific services over and over again, twice per entity in the change-setThis makes a lot of sense to me.
What are you worried about?