[英]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 API的Unity 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.