Entity Framework 6 - Unity 的依赖注入 - 存储库模式 - 添加或更新多对多关系的异常
Entity Framework 6 - Dependency Injection with Unity - Repository pattern - Add or Update exception for many to many relationship
在 Entity Framework 中添加具有多对多映射的新值时遇到问题。我知道 unit of work pattern
,但在我们的解决方案中,我们希望保留一个简单的存储库模式,而不是包含所有内容的工作单元 class。这是可能的还是我应该立即实施 Unit of Work
?
如果我不使用下面的 iSupplierRepository,将添加一个供应商,但它总是会添加一个 new,即使已经存在一个具有该名称的供应商。
错误:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
存储库示例:
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; }
}
更新:改用 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();
}
这样解决了,依赖注入来自教程Dependency Injection in ASP.NET Web API 2
。
https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
App_Start -> WebApiConfig
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
统一配置:
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);
}
如果安装 Unity bootstrapper for ASP.NET Web API package, a 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);
}
在 Entity Framework 中添加具有多对多映射的新值时遇到问题。我知道 unit of work pattern
,但在我们的解决方案中,我们希望保留一个简单的存储库模式,而不是包含所有内容的工作单元 class。这是可能的还是我应该立即实施 Unit of Work
?
如果我不使用下面的 iSupplierRepository,将添加一个供应商,但它总是会添加一个 new,即使已经存在一个具有该名称的供应商。
错误:
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
存储库示例:
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; }
}
更新:改用 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();
}
这样解决了,依赖注入来自教程Dependency Injection in ASP.NET Web API 2
。
https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
App_Start -> WebApiConfig
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
统一配置:
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);
}
如果安装 Unity bootstrapper for ASP.NET Web API package, a 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);
}