简体   繁体   中英

Design Patterns to resolve dependency using Autofac for Entity Framework TPH inheritance

I have used TPH in Entity Framework to create the Inventory table in my database.

public abstract class Inventory
{
    public Guid Id { get; set; }
    public DateTime On { get; set; }
}
public class CycleCountInventory : Inventory
{
    public string Frequency { get; set; }
    // and other properties
}
public class SkuGroupInventory : Inventory
{
    public string SkuGroup { get; set; }
    // and other properties
}

I have a requirement where items can be added to the inventory. However, the behaviour of the inventory repo should be different based on the type of inventory. I have a service class that holds a reference to the repo as follows:

public class InventoryService : IInventoryService
{

    public bool AddItems(Inventory inventory, IList<Guid> itemsGuidList)
    {
        // the inventory type is only known at this point

        IInventoryRepo repo = (inventory is SkuGroupInventory)
            ? (IInventoryRepo) new SkuGroupInventoryRepo()
            : new CycleCountInventoryRepo();

        return repo.PerformInventory(itemsGuidList);
    }

}

Currently I am loading the different implementations of IInventoryRepo by manually doing a check. There are a few problems with this approach:
1. I have using Autofac to resolve all dependencies... this approach makes my code hard to test
2. As more types of inventories are added, my code will become hard to manage

Are there any patterns (Abstract Factory / Strategy etc) I can use to delegate resolving the dependencies back to Autofac. I am sure someone would have faced this before. Thanks!

You can use a factory pattern here.

public interface IInventoryFactory 
{
 // instead of type, you could also have a discriminator on the
 // inventory class, to give its specific type. (enum etc.)
 IInventoryRepo CreateInventoryRepo(Type inventoryType);
}

public class MyInventoryFactory : IInventoryFactory 
{
 // instead of type, you could also have a discriminator on the
 // inventory class, to give its specific type. (enum etc.)
 public IInventoryRepo CreateInventoryRepo(Type inventoryType)
 {
   IInventoryRepo repo = (inventoryType == typeof(SkuGroupInventory))
        ? (IInventoryRepo) new SkuGroupInventoryRepo()
        : new CycleCountInventoryRepo();

   return repo;
 }
}

public class InventoryService : IInventoryService
{
    private readonly IInventoryFactory _inventoryFactory;

    public InventoryService(IInventoryFactory inventoryFactory)
    {
     _inventoryFactory = inventoryFactory;
    }

    public bool AddItems(Inventory inventory, IList<Guid> itemsGuidList)
    {
        // create the right repo based on type of inventory.
        // defer it to the factory

        IInventoryRepo repo = _inventoryFactory.CreateInventoryRepo(inventory.GetType());

        return repo.PerformInventory(itemsGuidList);
    }
 }

The real concrete implementation of the factory will be injected by Autofac, so register it in Autofac. This Service class method is now unit testable since you can mock the factory.

And the factory itself is unit testable since you just need to pass in the appropriate 'inventory' instances to assert the right concrete repos.

ps please note that Autofac doesn't even need the factory interface/concrete class. it can inject automatic factories for you. But it is just a preference of people to use Autofac automatic factories vs explicit interface based factories. Whichever you prefer.

also, a GetType is slightly performance intensive call.. in production scenarios, if your AddItems method is called several thousand times, a GetType is not a good FIRST option.. a lot of folks solve it as follows:

  1. Add an abstract getter only enum called inventoryType to the abstract Inventory Class

  2. Have the enum definition values as CycleCount, SkuGroup etc.

  3. Override the enum getter in the concrete child classes with their specific types.

  4. Once this happens, the CreateInventoryRepo method can take this enum as the parameter.

 public IInventoryRepo CreateInventoryRepo(InventoryType inventoryType) { IInventoryRepo repo = (inventoryType == InventoryType.SkuGroup) ? (IInventoryRepo) new SkuGroupInventoryRepo() : new CycleCountInventoryRepo(); return repo; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM