实体框架6 - 使用Unity的依赖注入 - 存储库模式 - 为多对多关系添加或更新例外

时间:2017-07-11 09:10:02

标签: c# entity-framework entity-framework-6 unity-container

在Entity Framework中添加具有多对多映射的新值时出现问题。我知道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);
    }

}

的ViewModels:

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; }
}

3 个答案:

答案 0 :(得分:3)

如果您安装了Unity bootstrapper for ASP.NET Web API软件包,则UnityHierarchicalDependencyResolver可用,它将为每个IHttpController解决方案使用新的子容器,从而有效地解决所有已注册HierarchicalLifetimeManager的注册请求,以便控制器中的所有存储库实例将使用相同的DbContext

NuGet包还将在App_Start中安装一些使用WebActivatorEx的引导代码。您可以使用此方法或更改以与您现在使用的内容保持一致。根据您发布的代码,它看起来像:

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);
}

答案 1 :(得分:0)

更新:请改用Randy Levy的答案。

我的建议是不要使用Repository或UoW。 EF已经实施了它们。您尝试重新实施它们时会遇到很多问题。

至于遇到异常的特定问题:您必须为实体使用相同的DbContext。同时,您不希望将DbContext用作Singleton,而是根据请求使用它。可能会找到一个可能的解决方案here

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();
}

答案 2 :(得分:0)

解决这个问题,依赖注入来自教程Dependency Injection in ASP.NET Web API 2

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection

App_Start - &gt; 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);
}