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();
}
答案 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)
显然,您的数据库具有Forms
和Products
。 Forms
和Products
之间存在一对多的关系:每个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
,您必须检查一下。