实体框架向Properties添加功能

时间:2015-02-18 19:20:44

标签: c# asp.net-mvc entity-framework entity-framework-6

在一个旧的WPF项目中,我有一个类似于Properties的类:

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

每次名称更改时,我都会确保该值已清除"。将它放在属性中可确保在设置对象的属性之前,我永远不会忘记清除字符串。

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

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

我可以通过Google找到的方法是通过MetadataType和部分类添加数据注释,但这并不能回答我的问题。

我尝试将上面的代码添加到分部类中但得到错误:

  

XXX类型已包含Name

的定义

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

3 个答案:

答案 0 :(得分:2)

免责声明:我还没有使用过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);

这会为您提供partial method,然后您可以根据需要实施。{3}}因此,对于您要自定义的任何属性,您现在可以向项目中添加另一个文件来执行此操作:

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

如果你不编写上面的代码块,那么partial方法就是一个no-op。 (部分方法必须返回void,因此使用ref参数)。

为什么不?

这种方法的优点是它对开发人员完全透明。该物业神奇地改变了。但有几个缺点:

有些控件希望如果他们打电话给姓名=" 123"如果他们得到了这个名字,那就是" 123"如果发生这种情况,将会失败。值正在更改但未触发PropertyChanged事件。如果您触发PropertyChanged,则有时会更改该值。这会导致无限循环。

没有对用户的反馈。他们打了一个东西,它看起来是正确的,但现在它说了一些不同的东西。某些控件可能会显示更改,而其他控件则表示不会。

开发者没有反馈意见。观察窗口似乎会改变价值观。并且在哪里看到验证规则并不明显。

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

您可能还想查看实体框架的更改跟踪在这种情况下的作用。如果属性集在从数据库加载值时进行清理,是否会考虑对实体进行更改? .Save()调用会将其写回数据库吗?这是否会导致从未打算更改数据库的代码突然这样做?

<强> ALTERNATIVE

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

答案 1 :(得分:1)

您可以通过创建实际在应用程序中使用的新属性来实现此目的。也许你可以隐藏设计师的原始属性。您使用的实际属性可能如下所示:

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

作为替代方案,您可以使用POCO类:

答案 2 :(得分:1)

  1. partial添加到生成的类中。
  2. 将生成的班级中Name的范围从public更改为internal
  3. 在同一个程序集中添加以下内容:
  4. 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 to Existing Database
    2. 修改

      任选地:

      1. 在生成的Name中将InternalName重命名为classname;用[Column("Name")]装饰它。
      2. 在您控制的CleanName中将Name重命名为partial class
      3. 4中的警告变为“每次重新生成POCO时都记得执行步骤1,2,和5 ”。
      4. 此方法的另一个好处是无需修改任何客户端代码(即使用Name仍为Name)。我仍然强烈考虑Code First to Existing Database