簡體   English   中英

實體框架6-具有Unity的依賴項注入-存儲庫模式-多對多關系的添加或更新異常

[英]Entity Framework 6 - Dependency Injection with Unity - Repository pattern - Add or Update exception for many to many relationship

在實體框架中添加具有多對多映射的新值時遇到問題。 我知道unit of work pattern但是在我們的解決方案中,我們希望保留一個簡單的存儲庫模式,而不是包含所有內容的工作單元類。 這是否可行,還是我應該立即實施Unit of Work

如果我不使用iSupplierRepository,則會添加一個供應商,但是即使已經存在一個具有該名稱的供應商,它也會始終添加一個供應商。

錯誤:

無法定義兩個對象之間的關系,因為它們已附加到不同的ObjectContext對象。

存儲庫示例:

public class SupplierRepository : IntEntityRepository<Supplier, DbContext>, ISupplierRepository
{
    public SupplierRepository(DbContext context) : base(context, context.Suppliers)
    {
    }
}

繼承的存儲庫:

public class IntEntityRepository<TEntity, TContext> : EntityRepository<TEntity, TContext, int>
    where TEntity : class, IEntity<int>
    where TContext : BaseIdentityDbContext
{
    public IntEntityRepository(TContext context, IDbSet<TEntity> set) : base(context, set)
    {
    }

    public override async Task<TEntity> GetAsync(int id)
    {
        return (await GetAsync(entity => entity.Id == id)).SingleOrDefault();
    }
...

 public abstract class EntityRepository<TEntity, TContext, TId> : IEntityRepository<TEntity, TId>
    where TEntity : class, IEntity<TId>
    where TContext : BaseIdentityDbContext
{
    protected TContext Context { get; }
    protected IDbSet<TEntity> Set { get; }

     protected EntityRepository(TContext context, IDbSet<TEntity> set)
     {
         Context = context;
         Set = set;
     }

     public abstract Task<TEntity> GetAsync(TId id);
...

統一:

container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();

控制器:

private readonly IContactRepository iContactRepository;
private readonly ISupplierRepository iSupplierRepository;

public ContactsController(IContactRepository iContactRepository, ISupplierRepository iSupplierRepository)
{
    this.iContactRepository = iContactRepository;
    this.iSupplierRepository = iSupplierRepository;
}

[HttpPut]
[Route("UpdateContact/{id}")]
public async Task<IHttpActionResult> UpdateContact(ContactViewModel contactVm, int id)
{
    try
    {
        var supplierList = new List<Supplier>();
        foreach (var contactVmSupplier in contactVm.Suppliers)
        {
            var supplier = await iSupplierRepository.GetAsync(contactVmSupplier.Id);
            supplierList.Add(supplier);
        }

        var contactOriginal = await iContactRepository.GetAsync(id);
        var updatedContact = Mapper.Map<ContactViewModel, Contact>(contactVm, contactOriginal);
        updatedContact.Suppliers = supplierList;

        await iContactRepository.UpdateAsync(updatedContact);
        return Ok();
    }
    catch (Exception e)
    {
        throw new Exception("Could not update a contact", e);
    }

}

視圖模型:

public class ContactViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<SupplierViewModel> Suppliers { get; set; }
}

public class SupplierViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }
}

楷模:

public class Contact : IEntity<int>
{
    public Contact()
    {
        Suppliers = new List<Supplier>();
    }

    [Key]
    public int Id { get; set; }

    public DateTime Created { get; set; }

    public DateTime Updated { get; set; }

    public string Name { get; set; }

    public ICollection<Supplier> Suppliers { get; set; }

}

public class Supplier: IEntity<int>
{
    public Supplier()
    {
        Contacts = new List<Contact>();
    }
    [Key]
    public int Id { get; set; }

    public DateTime Created { get; set; }

    public DateTime Updated { get; set; }

    public string Name { get; set; }

    public virtual ICollection<Contact> Contacts { get; set; }
}

如果您安裝了ASP.NET Web APIUnity bootstrapper ,將提供一個UnityHierarchicalDependencyResolver ,它將為每個IHttpController解析使用一個新的子容器, IHttpController有效地對每個請求進行HierarchicalLifetimeManager解析的所有注冊,以便控制器中的所有存儲庫實例都將使用相同的DbContext

NuGet軟件包還將在使用WebActivatorEx的App_Start中安裝一些引導代碼。 您可以使用此方法,也可以更改以與當前使用的內容保持一致。 根據您發布的代碼,它將類似於:

public static void ConfigureUnity(HttpConfiguration config)
{
    var container = new UnityContainer();
    container.RegisterType<DbContext>(new HierarchicalLifetimeManager());
    container.RegisterType<ISupplierRepository, SupplierRepository>();
    container.RegisterType<IContactRepository, ContactRepository>();
    config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
}

更新:改用蘭迪·利維的答案。


我的建議是根本不要使用存儲庫或UoW。 EF已經實施了它們。 嘗試重新實現它們時會遇到很多問題。

關於遇到的特定問題,您必須例外:您的實體必須使用相同的DbContext。 同時,您不想將DbContext用作Singleton,而是將其用於每個請求。 一個可能的解決方案可以在這里找到。

Application_BeginRequest(...)
{
  var childContainer = _container.CreateChildContainer();
  HttpContext.Items["container"] = childContainer;
  childContainer.RegisterType<ObjectContext, MyContext>
     (new ContainerControlledLifetimeManager());
}

Application_EndRequest(...)
{
  var container = HttpContext.Items["container"] as IUnityContainer
  if(container != null)
    container.Dispose();
}

像這樣解決了這個問題,依賴注入來自Dependency Injection in ASP.NET Web API 2教程。

https://docs.microsoft.com/zh-cn/aspnet/web-api/overview/advanced/dependency-injection

App_Start-> WebApiConfig

public static void Register(HttpConfiguration config)
{
    UnityConfig.ConfigureUnity(config);
...

UnityConfig:

public static void ConfigureUnity(HttpConfiguration config)
{
    var context = new DbContext();
    var container = new UnityContainer();
    container.RegisterType<ISupplierRepository, SupplierRepository>(new InjectionConstructor(context));
    container.RegisterType<IContactRepository, ContactRepository>(new InjectionConstructor(context));
    config.DependencyResolver = new UnityResolver(container);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM