在一个旧的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()函数,但这很脏,你永远不能确保其他开发人员(或我自己)会记得使用它们。
答案 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)
partial
添加到生成的类中。Name
的范围从public
更改为internal
。
public partial class classname
{
[NotMapped]
public string CleanName
{
get { return Name; }
set
{
var cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
Name = cleanName;
}
}
}
修改强>
任选地:
Name
中将InternalName
重命名为classname
;用[Column("Name")]
装饰它。CleanName
中将Name
重命名为partial class
。此方法的另一个好处是无需修改任何客户端代码(即使用Name
仍为Name
)。我仍然强烈考虑Code First to Existing Database。