Skip to content

Instantly share code, notes, and snippets.

@CallumVass
Last active January 2, 2016 11:59
Show Gist options
  • Save CallumVass/8300400 to your computer and use it in GitHub Desktop.
Save CallumVass/8300400 to your computer and use it in GitHub Desktop.
Hacky way to load the correct service for before/after save operations to provide additional validation / business logic to entities for Breeze JS
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");
}
}
}
@wardbell
Copy link

wardbell commented Jan 7, 2014

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment