一个一对多的关系,带孩子的父母

时间:2018-09-24 22:30:01

标签: c# entity-framework linq

public void getForm(string scode) {    
Form result = DBContext.Forms.Where(f => f.Code == fCode && f.SCode == sCode).FirstOrDefault();

    result.Products = result.Products.Where(p => p.Deleted== false).ToList(); // commenting this line fix the problem
return result;
}

我们如何将以上两行合并在一起,以避免出现以下错误。

  

操作失败:由于一个或多个外键属性不可为空,因此无法更改关系。对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

更新

在另一个函数中调用时,它会抛出错误

public void savetrans(string fcode)
{
                    Form form = GetForm(fCode);
                    var transDb = new DbContext.Data.Transaction()
                    {
                        FId = form.FId,
                        field1= "test",
                        field2= "test",
                        field3= "test",
                    };

                    DbContext.Transactions.Add(transactionDb);

                    DbContext.SaveChanges();
}

2 个答案:

答案 0 :(得分:0)

如果要删除标记为已删除的产品:

var deletedProducts = result.Products.Where(x => x.Deleted).ToList();
foreach(var deletedProduct in deletedProducts)
  result.Products.Remove(deletedProduct);

如果Form.Products是List<Product>,则可以使用.RemoveAll(x => x.Deleted)

如果您只想在与实体合作时排除已删除的产品,则建议为此目的使用未映射的属性:

public class Form
{
   public ICollection<Product> Products { get; set; } = new List<Product>();

   [NotMapped]
   public IReadOnlyCollection<Product> ActiveProducts => Products.Where(x => !x.Deleted).ToList().AsReadOnly();
   // or
   public IReadOnlyCollection<Product> ActiveProducts
   {
     get { return Products.Where(x => !x.Deleted).ToList().AsReadOnly();
   }
}

然后,当您只想使用有效产品时,请使用.ActiveProducts。这种方法的警告是您不能在EF Linq表达式中使用此属性,而只能以只读方式使用。例如,请勿尝试以下操作:

var products = context.Forms.Where(x => x.FormId == formId).SelectMany(x => x.ActiveProducts);

这将出错,因为EF不会映射ActiveProducts。您必须将.Products与“已删除”的相应过滤器配合使用。

我通常会在填充视图模型时而不是在实体级别上放置诸如处理活动/不活动之类的逻辑。实体应反映数据模型,而适用于您如何查看/与该逻辑交互的业务逻辑则由视图模型表示。使用视图模型的好处是,在填充视图模型时,只需检查一次“活动” /“已删除”状态,它们就不会使用在某些用途中无效的属性来毒害实体。

答案 1 :(得分:0)

显然,您的数据库具有FormsProductsFormsProducts之间存在一对多的关系:每个Form的零个或多个Products,每个Product恰好属于一个{{ 1}}使用外键(可能是Form

您的第一条语句会提取您的Product.FormId中满足某些要求的一个,如果没有这样的Forms,则为null。在不检查null返回值的情况下,您尝试更改此Form的{​​{1}}。

问题是,可能有多个Products对此Form具有不可为空的外键。它们是表单Products的ICollection中的项目。如果您将新的ICollection分配给Form,则实体框架希望将Products中的Form.Products的外键设置为零,指示此乘积不再属于任何形式。但是,在模型描述中,您定义了每个Products应该完全属于一个Form.Products。因此是错误。

所以您应该做什么,取决于您要在过程中执行的操作。您是否只想使用未删除的产品获取表单,然后应该执行查询。如果要从数据库中删除此表单中所有已删除的产品,则应执行更新

查询带有未删除产品的特定表单

Product

我需要创建一个新的Product对象的唯一原因是因为我想将其放在返回值中。如果不需要返回值,则可以将获取的属性放入匿名对象。这通常会更有效,因为您不会从数据库中获取不会使用的数据。

例如,上面的示例将分配Form。您将不需要它,因为您知道此表单的所有数千个产品将具有相同的FormId值:即public ICollection<Form> GetFormWithNonDeletedProducts(string scode) { using (var dbContext = new MyDbContext(...)) { return dBContext.Forms // from the collection of all Forms .Where(form => form.Code == fCode && form.SCode == sCode) // keep only the ones that I want .Select(form => new Form() // and create a new Form object { // with the properties I plan to use Id = form.Id, Name = form.Name, ... Products = form.Products // Fetch only the non-deleted products .Where(product => !product.Deleted) .ToList(), } .FirstOrDefault(); } } } 的值。

同一查询但未获取您不使用的属性(匿名类型)

Product.FormId

更新数据库:删除表格形式的已删除产品

尽管您的函数名为Form.Id,但似乎您想使用它来删除已删除的产品。

最简单的方法是使用using (var dbContext = new MyDbContext(...)) { return dBContext.Forms // from the collection of all Forms .Where(form => form.Code == fCode && form.SCode == sCode) // keep only the ones that I want .Select(form => new // and create a new Form object { // with the properties I plan to use Id = form.Id, Name = form.Name, ... Products = form.Products // Fetch only the non-deleted products .Where(product => !product.Deleted) .Select(product => new { // Select only properties you plan to use Name = product.Name, Price = product.Price, // not meaningful: you already know the value: // FormId = product.FormId, }) .ToList(), } .FirstOrDefault(); }

GetForms

可能必须要做DbSet<Products>.RemoveRange,您必须检查一下。