简体   繁体   English

实体框架向属性添加功能

[英]Entity Framework adding functionality to Properties

In an old WPF project I have a class with Properties like this: 在一个旧的WPF项目中,我有一个带有Properties的类,如下所示:

    private string _name = "";
    public string Name
    {
        get { return _name; }
        set
        {
            string cleanName = clsStringManip.CleanText(value, true);
            if (cleanName != _name)
            {
                _name = cleanName;
            }
        }
    }

Where every time the name changes, I ensure that the value is "cleaned". 每当名称更改时,我都确保该值是“ cleaned”的。 Putting it in the property ensures I never forget to clean the string before setting the property on the object. 将其放在属性中可确保我永远不会忘记在对象上设置属性之前清除字符串。

Now I am recreating this system using MVC5 and EntityFramework6.1 using DatabaseFirst. 现在,我使用MVC5和EntityFramework6.1和DatabaseFirst重新创建此系统。

So all the properties are autogenerated by EF. 因此,所有属性都是由EF自动生成的。 How then can I add the equivalent CleanText function to my properties without editing the autogen code? 然后如何在不编辑自动生成代码的情况下将等效的CleanText函数添加到属性中? - as I'll lose these changes next time I change my database and resync. -下次更改数据库并重新同步时,我将丢失这些更改。

All I can find via Google is a way add data annotations via MetadataType and partial classes but this doesn't answer my question. 我可以通过Google找到的所有方法都是通过MetadataType和局部类添加数据注释的方法,但这无法回答我的问题。

I tried to add the above code into a partial class but get the error: 我试图将以上代码添加到部分类中,但出现错误:

The type XXX already contains a definition for Name XXX类型已经包含名称的定义

The only way I can think is to create a bunch of SetProperty() functions but this is dirty and you can never ensure other developers (or myself) will remember to use them. 我能想到的唯一方法是创建一堆SetProperty()函数,但这很脏,您无法确保其他开发人员(或我自己)会记得使用它们。

Disclaimer: I haven't used EF 6 yet. 免责声明:我还没有使用过EF 6。

Let me answer this in two parts. 让我分两部分回答。 First, I will tell you how to do this. 首先,我将告诉您如何执行此操作。 Then I will tell you why I don't think you should do this. 然后,我将告诉您为什么我不认为您应该这样做。 :-) :-)

HOW: 怎么样:

As you discovered, you cannot create another Name property. 如发现的那样,您无法创建另一个Name属性。 You need to modify the way the EF generates the code, so that it gives you a place to insert your new code. 您需要修改EF生成代码的方式,以便为您提供插入新代码的位置。 Depending on how you are using the EF, it often generates Validate() method calls or OnPropertyChanged() calls. 根据您使用EF的方式,它通常会生成Validate()方法调用或OnPropertyChanged()调用。 You may be able to do what you want inside of those methods. 您可能可以在这些方法中执行所需的操作。

If you can't do this in Validate() or OnPropertyChanged() , you could change the T4 template to generate something like this: 如果您不能在Validate()OnPropertyChanged()执行此操作,则可以更改T4模板以生成如下内容:

private string _name = "";
public string Name
{
    get { return _name; }
    set
    {
        string cleanName = value;
        Cleanup_Name(ref cleanName);
        if (cleanName != _name)
        {
            _name = cleanName;
        }
    }
}

private partial void Cleanup_Name(ref string);

This gives you a partial method that you can then implement as you see fit. 这为您提供了部分方法 ,然后您可以视需要实现该方法 So for any property you want to customize, you can now add another file to your project that does this: 因此,对于您要自定义的任何属性,现在可以将另一个文件添加到您的项目中,以执行以下操作:

public partial class MyEntity {
   void Cleanup_Name(ref string name)
   {
      // Put your logic in here to fixup the name
   }
}

If you do not write the above code block, then the partial method is simply a no-op. 如果您不编写上述代码块,那么partial方法就是一个空操作。 (Partial methods must return void, hence the use of a ref parameter). (部分方法必须返回void,因此要使用ref参数)。

WHY NOT? 为什么不?

The advantage of this method is that it is totally transparent to the developer. 这种方法的优点是对开发人员完全透明。 The property is just magically changed. 该属性只是神奇地更改。 But there are several disadvantages: 但是有几个缺点:

Some controls expect that if they call name = "123" that if they get the name back, it is "123" and will fail if this happens. 某些控件希望,如果他们调用name =“ 123”,则如果将其取回,则为“ 123”,并且如果发生这种情况将失败。 Values are changing but no PropertyChanged event fired. 值正在更改,但不会触发PropertyChanged事件。 If you do fire the PropertyChanged , then they sometimes change the value back. 如果您确实触发了PropertyChanged ,则有时它们会将值改回来。 This can cause infinite loops. 这可能会导致无限循环。

There is no feedback to the user. 没有反馈给用户。 They typed in one thing, and it looked right, but now it says something different. 他们输入了一件事,看起来不错,但现在却说了另一番话。 Some controls might show the change and others won't. 有些控件可能会显示更改,而其他控件则不会。

There is no feedback to the developer. 没有反馈给开发人员。 The watch window will seemingly change values. 监视窗口似乎将更改值。 And it is not obvious where to see the validation rules. 而且在哪里可以看到验证规则并不明显。

The entity-framework itself uses these methods when it loads data from the database. 实体框架本身从数据库加载数据时会使用这些方法。 So if the database already contains values that don't match the cleanup rules, it will clean them when loading from the database. 因此,如果数据库中已经包含与清除规则不匹配的值,则从数据库加载时将清除它们。 This can make LINQ queries misbehave depending on what logic is run on the SQL server and what logic is run in the C# code. 这可能导致LINQ查询行为异常,具体取决于在SQL Server上运行的是哪种逻辑以及在C#代码中运行的是哪种逻辑。 The SQL code will see one value, the C# will see another. SQL代码将看到一个值,C#将看到另一个值。

You might also want to look into what the Entity-Framework's change tracking does in this case. 在这种情况下,您可能还想研究实体框架的更改跟踪。 If a property set does a cleanup while loading values from the database, does it consider that a change to the entity? 如果属性集在从数据库加载值时进行清理,那么它是否认为对实体的更改? Will a .Save() call write it back to the database? .Save()调用会把它写回到数据库吗? Could this cause code that never intended to change the database to suddenly do so? 这会导致原本不打算更改数据库的代码突然这样做吗?

ALTERNATIVE 备选

Instead of doing this, I suggest creating a Validate() method that looks at each property and returns errors indicating what is wrong. 我不建议这样做,而是建议创建一个Validate()方法,该方法查看每个属性并返回指示错误的错误。 You could also even create a Cleanup() method that fixes the things that are wrong. 您甚至可以创建一个Cleanup()方法来修复错误的内容。 This means the cleanups are no longer transparent, so the developer must call them explicitly. 这意味着清除不再透明,因此开发人员必须显式调用它们。 But that is a good thing: the code isn't changing values without them realizing it. 但这是一件好事:代码不会在没有意识到的情况下更改值。 The person writing the business logic or the UI knows at what point the values will change, and can get a list of why. 编写业务逻辑或UI的人知道值将在什么时候更改,并可以获得原因列表。

The only way you can achieve this is by creating a new property you actually use in your application. 实现此目的的唯一方法是创建一个在应用程序中实际使用的新属性。 Perhaps you can hide the original property in the designer. 也许您可以在设计器中隐藏原始属性。 The actual property you use could look like this: 您使用的实际属性可能如下所示:

public string ExternalName
{
    get { return Name; }
    set
    {
        string cleanName = clsStringManip.CleanText(value, true);
        if (cleanName != Name)
        {
            Name = cleanName;
        }
    }
}

As an alternative, you can use POCO classes: 或者,可以使用POCO类:

  1. Add partial to the generated class. partial添加到生成的类。
  2. Change the scope of Name in the generated class from public to internal . 将生成的类中Name的范围从public更改为internal
  3. Add the following in the same assembly: 在同一程序集中添加以下内容:

public partial class classname
{
    [NotMapped]
    public string CleanName
    {
        get { return Name; }
        set
        {
            var cleanName = clsStringManip.CleanText(value, true);
            if (cleanName != Name)
                Name = cleanName;
        }
    }
}
  1. Caveat: you'd have to remember to do steps 1-2 every time you regenerated your POCOs ... I'd seriously consider Code First to Existing Database . 注意:每次重新生成POCO时,您都必须记住要执行第1-2步。我会认真考虑将Code First转换为现有数据库

EDIT 编辑

Optionally: 可选:

  1. Rename Name as InternalName in the generated classname ; 在生成的classname Name中将Name重命名为InternalName decorate it with [Column("Name")] . [Column("Name")]装饰它。
  2. Rename CleanName as Name in the partial class under your control. 在您控制下的partial class ,将CleanName重命名为Name
  3. Caveat in 4 becomes "remember to do steps 1, 2, and 5 every time you regenerate POCOs". 4中的警告变成“每次重新生成POCO时都要记住执行步骤1、2 和5 ”。

This approach has the added benefit of not having to modify any of your client code (ie, use of Name remains Name ). 这种方法的另一个好处是不必修改任何客户端代码(即,使用Name仍然是Name )。 And I'd still strongly consider Code First to Existing Database . 而且我仍然会强烈考虑将Code First应用于现有数据库

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM