C# 实体框架与领域驱动设计
在花了几天时间尝试用EF和DDD建立一个简单的应用程序之后,我不得不说我感到非常沮丧,并且认为我最好还是使用LINQtoSQL,忘掉DDD和EF 带EF a) 您不能有正确的只读集合 b) 当您从子项集合中删除某些内容时,您经常会发现关系无法更改,因为一个或多个外键属性是不可为null的消息 c) 没有简单的方法可以删除父项的所有子项并重新插入它们 考虑到我发现的解决办法看起来相当糟糕,所有这些对我来说几乎都是画龙点睛。是否有人设法建立了一个简单的存储库来解决这些问题 如果是的话,你能分享一些代码吗 另外,我知道这是一个大话题,有没有人亲身体验过大规模web应用程序中DDD的任何实际好处?我们都知道这个理论,但如果它真的值得争论的话,有一个想法就好了C# 实体框架与领域驱动设计,c#,entity-framework,domain-driven-design,C#,Entity Framework,Domain Driven Design,在花了几天时间尝试用EF和DDD建立一个简单的应用程序之后,我不得不说我感到非常沮丧,并且认为我最好还是使用LINQtoSQL,忘掉DDD和EF 带EF a) 您不能有正确的只读集合 b) 当您从子项集合中删除某些内容时,您经常会发现关系无法更改,因为一个或多个外键属性是不可为null的消息 c) 没有简单的方法可以删除父项的所有子项并重新插入它们 考虑到我发现的解决办法看起来相当糟糕,所有这些对我来说几乎都是画龙点睛。是否有人设法建立了一个简单的存储库来解决这些问题 如果是的话,你能分享一些代
好的,到目前为止,我能做的最好的事情就是使用 AsNoTracking()当我查询某些内容时。这样我就可以得到我的信息,EF就不用做任何事情了 不管它在我背后干什么。我现在可以从收藏中删除,我可以 还可以删除(谁会认为我必须为此返回sql!) 有人知道使用AsNoTracking有什么陷阱吗?据我所知,基于SQL的 在我的对象上,并填充它们或更新/删除它们,我很好。整个追踪过程 还是走得太远了
名称空间EShop.Models.Repositories
{
公共类CustomerRepository:BaseRepository,IRepository
{
public CustomerRepository():base(新的EShopData()){}
#区域核方法
公共作废插入更新(客户)
{
如果(customer.CustomerId>0)
{
//您不能使用删除,如果您使用,您将附加,然后您将与下面的地址/卡有问题
//dbContext.Entry(address).State=EntityState.Added;将失败
dbContext.Database.ExecuteSqlCommand(“从CustomerAddress中删除,其中CustomerId=@CustomerId”,新的SqlParameter(“CustomerId”,customer.CustomerId));
dbContext.Database.ExecuteSqlCommand(“从信用卡中删除,其中CustomerId=@CustomerId”,新的SqlParameter(“CustomerId”,customer.CustomerId));
foreach(customer.Addresses中的var地址)
条目(地址).State=EntityState.Added;
foreach(客户信用卡中的var卡)
dbContext.Entry(card.State=EntityState.Added;
dbContext.Entry(customer.State=EntityState.Modified;
}
其他的
{
dbContext.Entry(customer.State=EntityState.Added;
foreach(客户信用卡中的var卡)
dbContext.Entry(card.State=EntityState.Added;
foreach(customer.Addresses中的var地址)
条目(地址).State=EntityState.Added;
}
}
公共作废删除(int customerId)
{
var existingCustomer=dbContext.Customers.Find(customerId);
if(existingCustomer!=null)
{
//删除卡片
var creditCards=dbContext.creditCards.Where(c=>c.CustomerId==CustomerId);
foreach(信用卡中的var卡)
dbContext.Entry(card.State=EntityState.Deleted;
//删除地址
var addresses=dbContext.CustomerAddresses.Where(c=>c.CustomerId==CustomerId);
foreach(地址中的变量地址)
dbContext.Entry(address).State=EntityState.Deleted;
//删除篮
条目(existingCustomer).State=EntityState.Deleted;
}
}
公共客户GetById(int customerId)
{
返回dbContext.Customers.Include(“地址”).AsNoTracking().SingleOrDefault(c=>c.CustomerId==CustomerId);
}
公共IList GetPagedAndSorted(int pageNumber、int pageSize、字符串排序方式、排序方向或排序方向)
{
返回null;
}
公共作废保存()
{
dbContext.SaveChanges();
}
#端域核方法
#区域附加法
#端域附加方法
}
}对b的响应:创建数据库时,必须级联删除(即数据库也删除所有相关的子记录)或使外键为空。那你就不会犯那个错误了。这不是EF的错,而是关系数据库处理约束的方式。您可以在EDMX、代码或使用数据库端的DDL中对此进行配置。取决于您的决定,您是如何设置项目的 对c的回应:更普遍的感觉是,删除所有子项并重新插入听起来很容易出错,而且有一种“味道”。至少只有在绝对必要的情况下我才会这么做。从性能的角度来看,更新可能更快。也许你可以重新思考一下为什么选择删除并重新插入的问题?a)你首先想做什么?您不能将集合设置为私有,并仅公开对其进行快照的公共属性吗 b) 要从数据库中删除子实体,请使用
dbcontext.thaentityset.remove(child)
,而不是parent.Children.remove(child)
或者,您可以通过将子对象中的外键作为主键的一部分来建立标识关系。然后parent.Children.Remove(child)
将从数据库中删除一行
c) 看来你在做傻事。如果您提供详细信息,我将提出不同的解决方案
大话题:你的领域够复杂吗?或者你只是想申请。。。在简单的CRUD应用程序中强制使用DDD模式?什么商业规则
namespace EShop.Models.Repositories
{
public class CustomerRepository : BaseRepository, IRepository<Customer, Int32>
{
public CustomerRepository() : base(new EShopData()) { }
#region CoreMethods
public void InsertOrUpdate(Customer customer)
{
if (customer.CustomerId > 0)
{
// you cannot use remove, if you do you ll attach and then you ll have issues with the address/cards below
// dbContext.Entry<CustomerAddress>(address).State = EntityState.Added; will fail
dbContext.Database.ExecuteSqlCommand("DELETE FROM CustomerAddress WHERE CustomerId = @CustomerId", new SqlParameter("CustomerId", customer.CustomerId));
dbContext.Database.ExecuteSqlCommand("DELETE FROM CreditCard WHERE CustomerId = @CustomerId", new SqlParameter("CustomerId", customer.CustomerId));
foreach (var address in customer.Addresses)
dbContext.Entry<CustomerAddress>(address).State = EntityState.Added;
foreach (var card in customer.CreditCards)
dbContext.Entry<CreditCard>(card).State = EntityState.Added;
dbContext.Entry<Customer>(customer).State = EntityState.Modified;
}
else
{
dbContext.Entry<Customer>(customer).State = EntityState.Added;
foreach (var card in customer.CreditCards)
dbContext.Entry<CreditCard>(card).State = EntityState.Added;
foreach (var address in customer.Addresses)
dbContext.Entry<CustomerAddress>(address).State = EntityState.Added;
}
}
public void Delete(int customerId)
{
var existingCustomer = dbContext.Customers.Find(customerId);
if (existingCustomer != null)
{
//delete cards
var creditCards = dbContext.CreditCards.Where(c => c.CustomerId == customerId);
foreach (var card in creditCards)
dbContext.Entry<CreditCard>(card).State = EntityState.Deleted;
//delete addresses
var addresses = dbContext.CustomerAddresses.Where(c => c.CustomerId == customerId);
foreach (var address in addresses)
dbContext.Entry<CustomerAddress>(address).State = EntityState.Deleted;
//delete basket
dbContext.Entry<Customer>(existingCustomer).State = EntityState.Deleted;
}
}
public Customer GetById(int customerId)
{
return dbContext.Customers.Include("Addresses").AsNoTracking().SingleOrDefault(c => c.CustomerId == customerId);
}
public IList<Customer> GetPagedAndSorted(int pageNumber, int pageSize, string sortBy, SortDirection sortDirection)
{
return null;
}
public void Save()
{
dbContext.SaveChanges();
}
#endregion CoreMethods
#region AdditionalMethods
#endregion AdditionalMethods
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Helpers;
using EShop.Models.DomainModel;
using System.Data;
using EShop.Models.DataAccess;
using System.Data.Objects;
using System.Data.Entity.Infrastructure;
namespace EShop.Models.Repositories
{
public class BasketRepository : BaseRepository, IRepository<Basket, Int32>
{
public BasketRepository() : base(new EShopData()) { }
#region CoreMethods
public void InsertOrUpdate(Basket basket)
{
var basketInDB = dbContext.Baskets.SingleOrDefault(b => b.BasketId == basket.BasketId);
if (basketInDB == null)
dbContext.Baskets.Add(basket);
}
public void Delete(int basketId)
{
var basket = this.GetById(basketId);
if (basket != null)
{
foreach (var product in basket.BasketProducts.ToList())
{
basket.BasketProducts.Remove(product); //delete relationship
dbContext.BasketProducts.Remove(product); //delete from DB
}
dbContext.Baskets.Remove(basket);
}
}
public Basket GetById(int basketId)
{
// eager-load product info
var basket = dbContext.Baskets.Include("BasketProducts")
.Include("BasketProducts.Product.Brand").SingleOrDefault(b => b.BasketId == basketId);
return basket;
}
public IList<Basket> GetPagedAndSorted(int pageNumber, int pageSize, string sortBy, SortDirection sortDirection)
{
throw new NotImplementedException();
}
public void Save()
{
dbContext.SaveChanges();
}
#endregion CoreMethods
#region AdditionalMethods
public void AddToBasket(Basket basket, Product product, int quantity)
{
var existingProductInBasket = dbContext.BasketProducts.Find(basket.BasketId, product.ProductId);
if (existingProductInBasket == null)
{
var basketProduct = new BasketProduct()
{
BasketId = basket.BasketId,
ProductId = product.ProductId,
Quantity = quantity
};
basket.BasketProducts.Add(basketProduct);
}
else
{
existingProductInBasket.Quantity = quantity;
}
}
public void RemoveFromBasket(Basket basket, Product product)
{
var existingProductInBasket = dbContext.BasketProducts.Find(basket.BasketId, product.ProductId);
if (existingProductInBasket != null)
{
basket.BasketProducts.Remove(existingProductInBasket); //delete relationship
dbContext.BasketProducts.Remove(existingProductInBasket); //delete from DB
}
}
public void RemoveFromBasket(BasketProduct basketProduct)
{
var basket = dbContext.Baskets.Find(basketProduct.BasketId);
var existingProductInBasket = dbContext.BasketProducts.Find(basketProduct.BasketId, basketProduct.ProductId);
if (basket != null && existingProductInBasket != null)
{
basket.BasketProducts.Remove(existingProductInBasket); //delete relationship
dbContext.BasketProducts.Remove(existingProductInBasket); //delete from DB
}
}
public void ClearBasket(Basket basket)
{
foreach (var product in basket.BasketProducts.ToList())
basket.BasketProducts.Remove(product);
}
#endregion AdditionalMethods
}
public partial class Basket
{
public Basket()
{
this.BasketProducts = new List<BasketProduct>();
}
public int BasketId { get; set; }
public int? CustomerId { get; set; }
public decimal TotalValue { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public ICollection<BasketProduct> BasketProducts { get; private set; }
public void AddToBasket(Product product, int quantity)
{
//BUSINESS LOGIC HERE
var productInBasket = BasketProducts.SingleOrDefault(b => b.BasketId == this.BasketId && b.ProductId == product.ProductId);
if (productInBasket == null)
{
BasketProducts.Add(new BasketProduct
{
BasketId = this.BasketId,
ProductId = product.ProductId,
Quantity = quantity
});
}
else
{
productInBasket.Quantity = quantity;
}
}
public void RemoveFromBasket(Product product)
{
//BUSINESS LOGIC HERE
var prodToRemove = BasketProducts.SingleOrDefault(b => b.BasketId == this.BasketId && b.ProductId == product.ProductId);
BasketProducts.Remove(prodToRemove);
}
}
public class BasketRepository : BaseRepository, IRepository<Basket, Int32>
{
public BasketRepository() : base(new EShopData()) { }
#region CoreMethods
//public void InsertOrUpdate(Basket basket, bool persistNow = true) { }
public void Save(Basket basket, bool persistNow = true)
{
var basketInDB = dbContext.Baskets.SingleOrDefault(b => b.BasketId == basket.BasketId);
if (basketInDB == null)
dbContext.Baskets.Add(basket);
if (persistNow)
dbContext.SaveChanges();
}
public void Delete(int basketId, bool persistNow = true)
{
var basket = this.GetById(basketId);
if (basket != null)
{
foreach (var product in basket.BasketProducts.ToList())
{
basket.BasketProducts.Remove(product); //delete relationship
dbContext.BasketProducts.Remove(product); //delete from DB
}
dbContext.Baskets.Remove(basket);
}
if (persistNow)
dbContext.SaveChanges();
}
public Basket GetById(int basketId)
{
// eager-load product info
var basket = dbContext.Baskets.Include("BasketProducts")
.Include("BasketProducts.Product.Brand").SingleOrDefault(b => b.BasketId == basketId);
return basket;
}
public IList<Basket> GetPagedAndSorted(int pageNumber, int pageSize, string sortBy, SortDirection sortDirection)
{
throw new NotImplementedException();
}
public void SaveForUnitOfWork()
{
dbContext.SaveChanges();
}