Breeze 使用beforeSaveEntity和导航属性
我的应用程序允许用户创建产品及其计量单位(UOM)和条形码 在创建过程中,API会检查是否没有输入条形码,它会自动生成条形码。这很好,直到我决定添加重量产品,需要7位数的秤条形码Breeze 使用beforeSaveEntity和导航属性,breeze,Breeze,我的应用程序允许用户创建产品及其计量单位(UOM)和条形码 在创建过程中,API会检查是否没有输入条形码,它会自动生成条形码。这很好,直到我决定添加重量产品,需要7位数的秤条形码BeforeSaveEntity将询问产品类型是否为重量,然后生成7位条形码,否则将生成13位条形码 问题是,;检查父表时,我无法使其工作,以下是我的代码: 模型:(为了方便起见,我省略了不需要的属性。) 禁用classId检查时,按以下顺序插入: 产品=>单元=>条形码 在这种情况下,条形码实体应该提供其父单元的信息,
BeforeSaveEntity
将询问产品类型是否为重量,然后生成7位条形码,否则将生成13位条形码
问题是,;检查父表时,我无法使其工作,以下是我的代码:
模型:(为了方便起见,我省略了不需要的属性。)
禁用classId检查时,按以下顺序插入:
产品=>单元=>条形码
在这种情况下,条形码实体应该提供其父单元的信息,然后是产品的信息。我理解。我想提出一个强有力的主张,即您不应该使用惰性导航来做您正在尝试做的事情 我强烈反对几乎所有开发人员在
BeforeSave…
方法中使用Breeze EF上下文。我指的是保存更改后的条形码
实体的EF上下文
在保存过程中,Breeze上下文保留供Breeze自己使用。你不应该往里面放任何不该存钱的东西。其中包括由惰性导航检索的实体。我很幸运,你不会航行
为什么?安全性是最重要的原因。我真的不能相信客户提供的数据。当验证或操作客户端更改时,我应该从数据库本身获得真实信息
出于同样的原因,我总是忽略来自客户端的originalValues
对象中的值切勿将这些值用于验证。它们主要用于并发检查和帮助EF确定正确的保存顺序。originalValues
属性名称很重要-它们告诉EF要更新哪些列-但它们的值(FK和乐观并发属性值除外)无关紧要。再说一次,不要相信他们
显然,你必须相信一些客户的意见。。。否则你什么都救不了。但明智的做法是将信任的范围限制在验证逻辑允许该用户创建或更改的“安全值”上
我担心我们在文档中没有充分阐述这些观点。
你该怎么办?
我坚定地认为,您应该创建一个新的、独立的EF上下文,用于验证和查询。在保存请求的生存期内,其他保存前…
活动可以使用该上下文
我直接从数据库填充此只读EF上下文。我将它和查询到其中的所有实体与Breeze save上下文及其更改集实体完全隔离
在您的情况下,创建了只读上下文后,我将提取客户端提供的条形码
关键信息,并执行扩展查询以获取相关的单元
和产品
信息。然后,我将使用该信息来更新客户机提供的条形码
,如您所述
你不同意我吗?没有任何东西可以阻止您使用Breeze EF上下文显式加载相关属性。你就是不能偷懒
由此可知,Breeze不应引诱您延迟加载EntityInfos
中实体的导航,也不应引诱您将Breeze EF上下文用于准备要保存的最终实体集合之外的任何目的
p、 序列化是不允许延迟加载的另一个原因。EF成功保存更改后,Breeze将使用保存的实体准备一个保存结果,并将该结果返回给客户端。当Json.Net序列化保存结果时,它会沿着所有导航路径漫游,并序列化它找到的任何内容。如果启用了延迟加载,它将(缓慢地)从数据库中拉入大量相关实体并发送它们。这是极不可取的
p、 当然,我们可以在发布用于序列化的保存结果之前关闭“延迟加载”。但是,如果您急切地或懒散地将相关的单元
和产品
实体加载到Breeze EF上下文中,这些实体也将被序列化并在保存结果中发送到客户端。不太好
不要在Breeze上下文中放入任何不应该保存的内容当调用
BeforeSaveEntity
和BeforeSaveEntities
方法时,这些实体刚刚从JSON流中具体化。它们尚未添加到EF上下文中。它们的数据属性都已填充,但导航属性未填充。因此,您需要的关系还不可用
如果相关实体(条形码、单位和产品,在您的情况下)在同一更改集中,则需要使用before saveentities
(这使您可以访问完整的saveMap
)并手动在saveMap
中查找相关实体(即按类型和键查找)
如果所需的相关实体不在
saveMap
中,则需要从数据存储中找到它们。在EF中,您可以创建一个新的EF上下文,正如沃德在回答中所描述的那样。+1这是富有成效的。谢谢沃德给我的启发。我会照你的建议做的。是的。我也明白了。相关实体位于同一变更集中,但出于Ward提到的原因,我将创建一个单独的EF上下文。谢谢你,史蒂夫。
public class Product
{ public int Id { get; set; }
public ProductName { get; set; }
public int ClassId { get; set; }
public ICollection<Unit> Units { get; set; }
}
public class Unit
{
public int Id { get; set; }
[ForeignKey("Product")]
public int ProdId { get; set; }
public int PackId { get; set; }
public decimal PackUnits { get; set; }
public Product Product { get; set; }
public ICollection<Barcode> Barcodes { get; set; }
}
public class Barcode
{
public int Id { get; set; }
[ForeignKey("Unit")]
public int UnitId { get; set; }
public string Bcode { get; set; }
[DefaultValue("false")]
public bool IsSystemGenerated { get; set; }
public Unit Unit { get; set; }
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
if (entityInfo.Entity.GetType() == typeof(Barcode)
&& (entityInfo.EntityState == Breeze.ContextProvider.EntityState.Added || entityInfo.EntityState == Breeze.ContextProvider.EntityState.Modified))
{
var barcode = (Barcode)entityInfo.Entity;
var product = (Product)barcode.Unit.Product; // The problem is here
int classId = product.ClassId;// Hence, can't get this guy
string bcode = barcode.Bcode;
if (String.IsNullOrEmpty(bcode))
{
if (classId != 2) // Check if the product type is not weight
barcode.Bcode = GenerateBarcode();
else // Otherwise generate scale barcode
barcode.Bcode = GenerateScaleBarcode();
barcode.IsSystemGenerated = true;
}
return true;
}
else
return true;
}
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
return saveMap;
}
Object reference not set to an instance of an object