如何使用EF 4.x DbContext生成器获取属性更改通知

13 .net c# entity-framework inotifypropertychanged

我正在玩Entity Framework 4.3,所以我使用DbContext Generator来创建上下文和实体类.

使用默认的EF 4代码生成器模板,实体类实现INotifyPropertyChanged,还可以在属性设置器中添加ChangingChanged部分方法.

当我使用EF 4.x DbContext生成器时,如下图所示,实体类更轻,并且不包括任何跟踪属性更改的方法.

在此输入图像描述

这是一个例子:

//------------------------------------------------------------------------------
// <auto-generated>
//    This code was generated from a template.
//
//    Manual changes to this file may cause unexpected behavior in your application.
//    Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;

namespace SomeNamespace
{
    public partial class SomeTable
    {
        public SomeTable()
        {
            this.Children = new HashSet<Child>();
        }

        public long parent_id { get; set; }
        public long id { get; set; }
        public string filename { get; set; }
        public byte[] file_blob { get; set; }

        public virtual Parent Parent { get; set; }
        public virtual ICollection<Child> Children { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

我必须错过一个重要的难题,但我的搜索没有结果.所以我的问题是:如何使用EF 4.3生成包含属性更改通知的类型?

编辑

我完全同意@derape的答案; 但我很好奇为什么.tt当EF 4默认代码生成模板已经有钩子时我需要更改文件.我的意思是什么时候绑定到WPF DependencyProperty'?如果没有INotifyPropertyChanged,命令对一堆对象中的一组属性所做的更改将不会反映在UI中.我错过了什么?

And*_*ers 23

我最近偶然发现了这个问题,我编辑了我的Entity.tt来实现以下更改,快速补丁但效果很好..

将以下内容添加到CodeStringGenerator类

public string EntityClassOpening(EntityType entity)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1}partial class {2}{3} : {4}",
        Accessibility.ForType(entity),
        _code.SpaceAfter(_code.AbstractOption(entity)),
        _code.Escape(entity),
        _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)),
        "INotifyPropertyChanged");
}


public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} {2} {{ {3}{6} {4}{5} }}",
        Accessibility.ForProperty(edmProperty),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)),
        "set { _"+_code.Escape(edmProperty).ToLower()+" = value; OnPropertyChanged(\""+_code.Escape(edmProperty)+"\");}",
        "get { return _"+_code.Escape(edmProperty).ToLower()+"; }");

}
public string Private(EdmProperty edmProperty) {
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} _{2};",
        "private",
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty).ToLower());

}
Run Code Online (Sandbox Code Playgroud)

将以下内容添加到生成器中

using System.ComponentModel;
<#=codeStringGenerator.EntityClassOpening(entity)#>
{
<#
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);
var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);
var complexProperties = typeMapper.GetComplexProperties(entity);
#>

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
Run Code Online (Sandbox Code Playgroud)

并进一步下降

foreach (var edmProperty in simpleProperties)
{
#>
<#=codeStringGenerator.Private(edmProperty)#>
    <#=codeStringGenerator.Property(edmProperty)#>
<#
}


foreach(var complexProperty in complexProperties)
{
#>
<#=codeStringGenerator.Private(complexProperty)#>
    <#=codeStringGenerator.Property(complexProperty)#>
<#
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*hey 14

我创建了Anders答案的变体,但有以下不同之处:

  • Entity.tt文件的更改次数减少
  • 使用INotifyPropertyChanged实现的基类(方便引入其他常用功能)
  • 生成的模型类中的清洁代码布局

所以我的步骤是:

为要扩展的模型类创建基类:

public abstract class BaseModel : INotifyPropertyChanged
{
    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (object.Equals(storage, value)) return false;

        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢Juan Pable Gomez建议的改进!我喜欢你的改进,即使其他评论者没有:)

将Entity.tt文件中的EntityClassOpening方法更新为以下内容:

public string EntityClassOpening(EntityType entity)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1}partial class {2}{3}",
        Accessibility.ForType(entity),
        _code.SpaceAfter(_code.AbstractOption(entity)),
        _code.Escape(entity),
        _code.StringBefore(" : ", string.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)) ? "BaseModel" : _typeMapper.GetTypeName(entity.BaseType)));
}
Run Code Online (Sandbox Code Playgroud)

找到这一行:

<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
Run Code Online (Sandbox Code Playgroud)

并将其更新为:

<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> : BaseModel
Run Code Online (Sandbox Code Playgroud)

谢谢Manolo!

将Entity.tt文件中的Property方法更新为以下内容:

public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "private {1} {3};\r\n"+
        "\t{0} {1} {2} \r\n" +
        "\t{{ \r\n" +
            "\t\t{4}get {{ return {3}; }} \r\n" +
            "\t\t{5}set {{ SetProperty(ref {3}, value); }} \r\n" + 
        "\t}}\r\n",
        Accessibility.ForProperty(edmProperty),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        "_" + Char.ToLowerInvariant(_code.Escape(edmProperty)[0]) + _code.Escape(edmProperty).Substring(1),
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
Run Code Online (Sandbox Code Playgroud)

你完成了!现在你的模型类看起来像:

public partial class User : BaseModel
{
    private int _id;
    public int Id 
    { 
        get { return _id; } 
        set { SetProperty(ref _id,value);}
    }

    private string _name;
    public string Name 
    { 
        get { return _name; } 
        set { SetProperty(ref _name , value); }
    }
Run Code Online (Sandbox Code Playgroud)

如果您能看到其他改进,请随时(尝试)编辑此解决方案.

  • 你还必须找到以下行:`<#= Accessibility.ForType(complex)#> partial class <#= code.Escape(complex)#>`并添加`:BaseModel` (3认同)

Jua*_*mez 5

我试图编辑Brian Hinchey解决方案但是EDIT被拒绝了.然后我在这里发布我的addapts.

此解决方案为每个属性Setter生成较少的代码,利用CallerMemberName属性.

注意:我使用的是EF 6.1,它运行良好.

BaseClass现在看起来像这样.

public abstract class BaseModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (object.Equals(storage, value)) return false;

        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }


    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新Entity.tt中的EntityClassOpening方法,完全像Brian的那样:

public string EntityClassOpening(EntityType entity)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1}partial class {2}{3}",
        Accessibility.ForType(entity),
        _code.SpaceAfter(_code.AbstractOption(entity)),
        _code.Escape(entity),
        _code.StringBefore(" : ", string.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)) ? "BaseModel" : _typeMapper.GetTypeName(entity.BaseType)));
}
Run Code Online (Sandbox Code Playgroud)

正如布莱恩所说,记得改变:

<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
FOR 
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> : BaseModel
Run Code Online (Sandbox Code Playgroud)

我的最后一个改变是Property方法

public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "private {1} {3};\r\n"+
        "\t{0} {1} {2} \r\n" +
        "\t{{ \r\n" +
            "\t\t{4}get {{ return {3}; }} \r\n" +
            "\t\t{5}set {{ SetProperty(ref {3}, value); }} \r\n" + 
        "\t}}\r\n",
        Accessibility.ForProperty(edmProperty),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        "_" + Char.ToLowerInvariant(_code.Escape(edmProperty)[0]) + _code.Escape(edmProperty).Substring(1),
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
Run Code Online (Sandbox Code Playgroud)

最后这个类看起来像:

public partial class User : BaseModel
{
    private int _id;
    public int Id 
    { 
        get { return _id; } 
        set { SetProperty(ref _id , value);} 
    }

    private string _name;
    public string Name 
    { 
        get { return _name; } 
        set { SetProperty(ref _name , value);}
    } 
}
Run Code Online (Sandbox Code Playgroud)

这使得生成的clases更轻.

最近我在使用PropertyChanged.Fody库,但原因不明(至少对我而言)它有时无法正常工作.这就是我降落在这里的原因.这个解决方案(布莱恩的解决方案)每次都有效.

  • 感谢Juan的建议改进.我编辑了我的答案,包括你的建议. (3认同)

Der*_*Ape 0

好吧,这取决于你想做什么。如果您只想实现自定义属性/方法,您可以使用部分类的功能。如果您想更改实体设计器中属性的 setter/getter,则必须调整 dbContext 生成器模板文件。这是一个T4模板。