使用beforeSaveEntity和Navigation属性 [英] Working with beforeSaveEntity and Navigation Properties

查看:46
本文介绍了使用beforeSaveEntity和Navigation属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序允许用户创建产品以及其UOM(计量单位)和条形码
在创建过程中,API将检查是否未输入条形码,并会自动生成。效果很好,直到我决定添加需要7位数字秤条形码的重量产品。 BeforeSaveEntity 将询问产品类型是否为重量,然后生成7位条形码,否则,它将生成13位条形码。





模型:(为方便起见,我省略了

 公共类产品
{public int Id {get;组; }
public ProductName {get;组; }
public int ClassId {get;组; }
public ICollection< Unit>单位{get;组; }
}

public class Unit
{
public int Id {get;组; }
[ForeignKey( Product)]
public int ProdId {get;组; }
public int PackId {get;组; }
公共小数点PackUnits {get;组; }
公共产品产品{组; }
public ICollection< Barcode>条形码{get;组; }
}
公共类条形码
{
public int Id {get;组; }
[ForeignKey( Unit)]
public int UnitId {get;组; }
公共字符串Bcode {get;组; }
[DefaultValue( false)]
public bool IsSystemGenerated {get;组; }
public Unit Unit {get;组; }
}

保存实体之前:

 受保护的覆盖布尔值BeforeSaveEntity(EntityInfo entityInfo)
{
if(entityInfo.Entity.GetType()== typeof(条形码)
&&(entityInfo.EntityState == Breeze.ContextProvider.EntityState.Added || EntityInfo.EntityState == Breeze.ContextProvider.EntityState.Modified))
{
可变条形码=(条形码)entityInfo.Entity;
var product =(Product)barcode.Unit.Product; //问题出在这里

int classId = product.ClassId; //因此,无法获得此人

字符串bcode =条形码.Bcode;


if(String.IsNullOrEmpty(bcode))
{
if(classId!= 2)//检查产品类型是否不是砝码
条码.Bcode = GenerateBarcode();
else //否则生成比例条码
条码。Bcode= GenerateScaleBarcode();

条码.IsSystemGenerated = true;
}

返回true;
}
否则
返回true;
}

受保护的覆盖Dictionary< Type,List< EntityInfo>> BeforeSaveEntities(Dictionary< Type,List< EntityInfo>> saveMap)
{
return saveMap;
}

获取父导航属性 Product 给出错误消息:

 对象引用未设置为对象

禁用classId检查,插入按以下顺序进行:
产品=>单位=>条形码
其中在这种情况下,条形码实体应提供有关其父单位然后是产品的信息。

解决方案

我理解。我要强力主张不要使用懒惰导航来做您想做的事情。


我强烈建议几乎所有开发人员在<$ c $中使用Breeze EF上下文。 c> BeforeSave ... 方法。我指的是保存您已更改的条形码实体的EF上下文。


Breeze上下文保留用于Breeze的在保存过程中自行使用。您不应在其中放入任何不打算保存的东西。这包括通过惰性导航检索的实体。 IMO,很幸运,您无法导航。


为什么? 安全性是最重要的原因。我真的不能信任来自客户端的数据。验证或处理客户端更改时,我应该从数据库本身获取真相。


出于相同的原因,我总是忽略 (来自客户端的 originalValues 对象)。 请勿使用这些值进行验证。它们主要用于并发检查和帮助EF确定正确的保存顺序。 原始值属性名称​​很重要-它们告诉EF要更新的列-但是它们的(FK和乐观并发属性值是无关紧要的。再次,不要信任它们。


很明显,您必须信任客户端输入的 some ,否则您将无法什么都不要保存。但是将信任范围限制为安全值是明智的。您的验证逻辑允许该用户创建或更改。


我担心我们在文档中没有足够强调这些观点。


您应该怎么做?


我坚决认为,您应该提升新的,独立的EF环境 strong>用于验证和查询。在保存请求的生存期内,其他 BeforeSave ... 活动可以使用该上下文。


我填充此只读EF上下文直接来自数据库。我保留了它,所有查询到的实体都与Breeze保存上下文及其变更集实体完全隔离。


在您的情况下,创建了该只读上下文,我将提取客户端提供的条形码密钥信息,并进行扩展查询以获取相关的 Unit 产品信息。然后,我将使用该信息来更新您所描述的客户端提供的条形码


不同意我吗?没有什么可以阻止您使用Breeze EF上下文显式加载相关属性。您只是不能延迟加载。


因此,Breeze不应通过 EntityInfos ,并且不应诱使您出于准备保存实体的最终集合以外的任何目的而使用Breeze EF上下文。


ps 序列化是不允许延迟加载的另一个原因。当EF成功保存更改时,Breeze将使用保存的实体准备保存结果,并将此结果返回给客户端。当Json.Net序列化保存结果时,它将漫步于所有导航路径并序列化所有找到的内容。如果启用了延迟加载,它将(缓慢地)从数据库中提取大量相关实体,并将它们也发送出去。


p.p.s。当然,我们可以关闭延迟加载功能。在释放保存结果以进行序列化之前。但是,如果您急切或懒惰地将相关的 Unit Product 实体加载到Breeze EF Context中,这些也将是序列化并在保存结果中发送给客户端。不好。


请勿在Breeze上下文中放置不应保存的内容


My application allows the user to create products along with their UOM (Units of Measurement) and Barcodes During the creation process, API will check if there is no barcode entered, it will generate it automatically. That worked fine until I decided to add weight products that require scale barcodes with 7 digits. BeforeSaveEntity will ask if the product type is weight then generate 7 digits barcode, else, it will generate 13 digits.

The problem is; I can't get this to work when checking the parent table, here is my code:

Models: (for convenience, I have omitted unneeded properties.)

 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; }
    }

Before Save Entity:

     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;
    }

Acquiring the parent navigation property Product gives an error message:

Object reference not set to an instance of an object

Disabling the classId checking, Insert takes place in the following order: Product => Unit => Barcode which in this case, Barcode entity should give information on it's parents Unit then Product.

解决方案

I understand. I want to make a strong claim that you should not do what you are trying do using lazy navigation.

I strongly discourage almost all developer use of the Breeze EF context inside a BeforeSave... method. I'm referring specifically to the EF context that holds your changed barcode entity.

The Breeze context is reserved for Breeze's own use during the save process. You shouldn't put anything into it that isn't destined for save. That includes entities retrieved by lazy navigation. IMO it is fortunate that you can't navigate.

Why? Security is the most important reason. I can't really trust data from the client. When validating or manipulating client changes, I should get my truth from the database itself.

For the same reason I always ignore the values in the originalValues object that came from the client. Never use these values for validation. They are useful primarily for concurrency checking and to help EF figure out the proper save order. The originalValues property names are important - they tell EF which columns to update - but their values, other than FK and optimistic concurrency property values, are immaterial. Again, don't trust them.

Obviously you have to trust some of the client input ... otherwise you couldn't save anything. But it is wise to limit the scope of that trust to the "safe values" that your validation logic permits this user to create or change.

I fear we have not made these points strongly enough in our documentation.

What should you do?

I am of the firm opinion that you should spin up a new, separate EF context for use in validation and inquiry. That context can be used by other BeforeSave... activities during the lifetime of the save request.

I populate this read-only EF context directly from the database. I keep it and all of the entities queried into it completely isolated from the Breeze save-context and its change-set entities.

In your case, having created that read-only context, I'd extract the client-supplied barcode key information and do an expand query to get the related Unit and Product information. Then I'd use that information to update the client-supplied barcode as you've described.

Disagree with me? Nothing stops you from loading the related properties explicitly with the Breeze EF context. You just can't lazy load.

It follows from this that Breeze should not tempt you with lazy loading navigation of the entities in EntityInfos and should not tempt you into using the Breeze EF context for any purpose other than the preparation of the final collection of entities to be saved.

p.s. Serialization is another reason that lazy load is disallowed. When EF saves changes successfully, Breeze prepares a save result with the saved entities and returns this result to the client. When Json.Net serializes the save result, it wanders down all navigation paths and serializes whatever it finds. If lazy loading were enabled, it would (slowly) pull in tons of related entities from the database and send them too. That is highly undesirable.

p.p.s. Of course we could turn off "lazy load" just before releasing the save result for serialization. But if you had eagerly or lazy loaded the related Unit and Product entities into the Breeze EF Context, these too would be serialized and sent to the client in the save result. Not good.

Don't put anything in the Breeze context that isn't supposed to be saved.

这篇关于使用beforeSaveEntity和Navigation属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆