繁体   English   中英

实体框架向属性添加功能

[英]Entity Framework adding functionality to Properties

在一个旧的WPF项目中,我有一个带有Properties的类,如下所示:

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

每当名称更改时,我都确保该值是“ cleaned”的。 将其放在属性中可确保我永远不会忘记在对象上设置属性之前清除字符串。

现在,我使用MVC5和EntityFramework6.1和DatabaseFirst重新创建此系统。

因此,所有属性都是由EF自动生成的。 然后如何在不编辑自动生成代码的情况下将等效的CleanText函数添加到属性中? -下次更改数据库并重新同步时,我将丢失这些更改。

我可以通过Google找到的所有方法都是通过MetadataType和局部类添加数据注释的方法,但这无法回答我的问题。

我试图将以上代码添加到部分类中,但出现错误:

XXX类型已经包含名称的定义

我能想到的唯一方法是创建一堆SetProperty()函数,但这很脏,您无法确保其他开发人员(或我自己)会记得使用它们。

免责声明:我还没有使用过EF 6。

让我分两部分回答。 首先,我将告诉您如何执行此操作。 然后,我将告诉您为什么我不认为您应该这样做。 :-)

怎么样:

如发现的那样,您无法创建另一个Name属性。 您需要修改EF生成代码的方式,以便为您提供插入新代码的位置。 根据您使用EF的方式,它通常会生成Validate()方法调用或OnPropertyChanged()调用。 您可能可以在这些方法中执行所需的操作。

如果您不能在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);

这为您提供了部分方法 ,然后您可以视需要实现该方法 因此,对于您要自定义的任何属性,现在可以将另一个文件添加到您的项目中,以执行以下操作:

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

如果您不编写上述代码块,那么partial方法就是一个空操作。 (部分方法必须返回void,因此要使用ref参数)。

为什么不?

这种方法的优点是对开发人员完全透明。 该属性只是神奇地更改。 但是有几个缺点:

某些控件希望,如果他们调用name =“ 123”,则如果将其取回,则为“ 123”,并且如果发生这种情况将失败。 值正在更改,但不会触发PropertyChanged事件。 如果您确实触发了PropertyChanged ,则有时它们会将值改回来。 这可能会导致无限循环。

没有反馈给用户。 他们输入了一件事,看起来不错,但现在却说了另一番话。 有些控件可能会显示更改,而其他控件则不会。

没有反馈给开发人员。 监视窗口似乎将更改值。 而且在哪里可以看到验证规则并不明显。

实体框架本身从数据库加载数据时会使用这些方法。 因此,如果数据库中已经包含与清除规则不匹配的值,则从数据库加载时将清除它们。 这可能导致LINQ查询行为异常,具体取决于在SQL Server上运行的是哪种逻辑以及在C#代码中运行的是哪种逻辑。 SQL代码将看到一个值,C#将看到另一个值。

在这种情况下,您可能还想研究实体框架的更改跟踪。 如果属性集在从数据库加载值时进行清理,那么它是否认为对实体的更改? .Save()调用会把它写回到数据库吗? 这会导致原本不打算更改数据库的代码突然这样做吗?

备选

我不建议这样做,而是建议创建一个Validate()方法,该方法查看每个属性并返回指示错误的错误。 您甚至可以创建一个Cleanup()方法来修复错误的内容。 这意味着清除不再透明,因此开发人员必须显式调用它们。 但这是一件好事:代码不会在没有意识到的情况下更改值。 编写业务逻辑或UI的人知道值将在什么时候更改,并可以获得原因列表。

实现此目的的唯一方法是创建一个在应用程序中实际使用的新属性。 也许您可以在设计器中隐藏原始属性。 您使用的实际属性可能如下所示:

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

或者,可以使用POCO类:

  1. partial添加到生成的类。
  2. 将生成的类中Name的范围从public更改为internal
  3. 在同一程序集中添加以下内容:

public partial class classname
{
    [NotMapped]
    public string CleanName
    {
        get { return Name; }
        set
        {
            var cleanName = clsStringManip.CleanText(value, true);
            if (cleanName != Name)
                Name = cleanName;
        }
    }
}
  1. 注意:每次重新生成POCO时,您都必须记住要执行第1-2步。我会认真考虑将Code First转换为现有数据库

编辑

可选:

  1. 在生成的classname Name中将Name重命名为InternalName [Column("Name")]装饰它。
  2. 在您控制下的partial class ,将CleanName重命名为Name
  3. 4中的警告变成“每次重新生成POCO时都要记住执行步骤1、2 和5 ”。

这种方法的另一个好处是不必修改任何客户端代码(即,使用Name仍然是Name )。 而且我仍然会强烈考虑将Code First应用于现有数据库

暂无
暂无

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

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