wpf - Entity Framework 4 - 关联通知

标签 wpf entity-framework-4.1 inotifypropertychanged

我在使用 EF 4、外键和 INotifyPropertyChanged/为标量属性公开的部分方法时遇到了一些困难。

希望您能帮我找到正确的方法。

图片我有一个 Customer 实体,与 Country 实体有 *..1 关系。

现在,我显然希望能够做到:

var customer = new Customer();
customer.Country = [...]

...但我不一定需要 CountryKey 属性。

我在 .edmx 设计器中使用正确的基数在 EF 中创建了一个关联。我选择不在对话框中“添加外键属性”。

这给我留下了一个没有部分 OnCountryChanging 和 OnCountryChanged 的​​生成类。

接下来,我尝试添加外键属性,现在我有了 OnCountryKeyChanging 和 OnCountryKeyChanged。

但是,生成的代码如下所示:

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int64 CountryKey
{
    get
    {
        return _CountryKey;
    }
    set
    {
        OnCountryKeyChanging(value);
        ReportPropertyChanging("CountryKey");
        _CountryKey = StructuralObject.SetValidValue(value);
        ReportPropertyChanged("CountryKey");
        OnCountryKeyChanged();
    }
}
private global::System.Int64 _CountryKey;
partial void OnCountryKeyChanging(global::System.Int64 value);
partial void OnCountryKeyChanged();

正如您从生成的代码中看到的那样,PropertyChanged 通知出现时使用“CountryKey”而不是“Country”。这使得 WPF 中的数据绑定(bind)变得困难。

我的问题是:我该如何解决这个问题?

  • 我是否将我的对象包装在 ViewModel 中,监听属性变化并去除“键”部分?
  • 我是否修改 T4 模板?
  • 或者是否有第三种选择我还看不到?

我非常感谢这里的任何建议,因为我正在试验 WPF/EF 而没有将每个模型属性包装在 ViewModel 中。

最佳答案

“最佳实践”方法是在 View 模型中装饰您的模型,根据需要公开模型属性。您可以使用动态对象创建一个通用的 ViewModel,可能使用属性映射等。

public class DynamicViewModel : DynamicObject, INotifyPropertyChanged, IDisposable
{
    public event PropertyChangedEventHandler PropertyChanged;

    private static readonly Dictionary<Type, Tuple<Dictionary<string, PropertyInfo>, Dictionary<string, string>>> typeDictionary = new Dictionary<Type, Tuple<Dictionary<string, PropertyInfo>, Dictionary<string, string>>>();

    private readonly Dictionary<string, object> additionalProperties = new Dictionary<string, object>();

    private readonly object underlyingObject;
    public object UnderlyingObject
    {
        get
        {
            return underlyingObject;
        }
    }

    private readonly Type type;

    /// <summary>
    /// constructor which takes a model for which it will be extensing its perceived properties
    /// </summary>
    /// <param name="underlyingObject">the underlying object</param>
    public DynamicViewModel(IBindableRfqViewModel underlyingObject) : this(underlyingObject, new Dictionary<string, string>())
    {

    }

    /// <summary>
    /// constructor which takes a model for which it will be extensing its perceived properties as well as a property map
    /// </summary>
    /// <param name="underlyingObject">the underlying object</param>
    /// <param name="propertyMap">a string/string dictionary, where the key is a property on the underlying object, and the value is the name of the dynamic property to be used as a binding target</param>
    public DynamicViewModel(IBindableRfqViewModel underlyingObject, Dictionary<string, string> propertyMap)
    {
        this.underlyingObject = underlyingObject;
        if (underlyingObject is INotifyPropertyChanged)
        {
            ((INotifyPropertyChanged)underlyingObject).PropertyChanged += OnUnderlyingPropertyChanged;
        }

        type = underlyingObject.GetType();
        if (typeDictionary.ContainsKey(type))
        {
            return;
        }
        lock (typeDictionary)
        {
            if (typeDictionary.ContainsKey(type))
            {
                return;
            }
            var forwardPropertyMap = propertyMap;
            var typeProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, p => p);
            typeDictionary.Add(type, Tuple.Create(typeProperties,forwardPropertyMap));
        }
    }

    private void OnUnderlyingPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.OnPropertyChanged(e.PropertyName);
    }

    private bool TryGetProperty(string name, out object result)
    {
        try
        {
            var propertyData = typeDictionary[type];
            var modelProperty = name;
            if (propertyData.Item2.ContainsKey(name))
            {
                modelProperty = propertyData.Item2[name];
            }
            if (propertyData.Item1.ContainsKey(modelProperty))
            {
                result = propertyData.Item1[modelProperty].GetValue(underlyingObject, null);
                return true;
            }

            if (additionalProperties.ContainsKey(name))
            {
                result = additionalProperties[name];
                return true;
            }

            result = null;
            return true;
        }
        catch (Exception ex)
        {
            result = null;
            return false;
        }
    }

    /// <summary>
    /// <see cref="DynamicObject.TryGetMember" />
    /// </summary>
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return this.TryGetProperty(binder.Name, out result);
    }

    /// <summary>
    /// <see cref="DynamicObject.TryGetIndex" />
    /// </summary>
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        return this.TryGetProperty(indexes[0].ToString(), out result);
    }

    private bool TrySetProperty(string name, object value)
    {
        try
        {
            var propertyData = typeDictionary[type];
            var modelProperty = name;
            if (propertyData.Item2.ContainsKey(name))
            {
                modelProperty = propertyData.Item2[name];
            }
            if (propertyData.Item1.ContainsKey(modelProperty))
            {
                propertyData.Item1[modelProperty].SetValue(underlyingObject, value, null);
            }
            else
            {
                if (!additionalProperties.ContainsKey(name))
                {
                    additionalProperties.Add(name, new object());
                }
                additionalProperties[name] = value;
            }

            this.OnPropertyChanged(name);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }

    }

    /// <summary>
    /// <see cref="DynamicObject.TrySetMember" />
    /// </summary>
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        return this.TrySetProperty(binder.Name, value);
    }

    /// <summary>
    /// <see cref="DynamicObject.TrySetIndex" />
    /// </summary>
    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
    {
        return indexes.Length == 0 || this.TrySetProperty(indexes[0].ToString(), value);
    }

    private void OnPropertyChanged(string propName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }

    /// <summary>
    /// IDisposable implementation
    /// </summary>
    public void Dispose()
    {
        if (underlyingObject is INotifyPropertyChanged)
        {
            ((INotifyPropertyChanged)underlyingObject).PropertyChanged -= OnUnderlyingPropertyChanged;
        }
        if (underlyingObject is IDisposable)
        {
            ((IDisposable)underlyingObject).Dispose();
        }
    }
}

关于wpf - Entity Framework 4 - 关联通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10306812/

相关文章:

c# - 如何从ViewModel调用NavigationService

c# - 如何在 Entity Framework 中只加载基本类型

c# - Database.SetInitializer 中的 DropCreateDatabaseIfModelChanges

c# - 控制不立即使用 INotifyPropertyChanged 更新绑定(bind)属性

wpf - INotifyPropertyChanged 或 INotifyCollectionChanged 与 DataTable 一起使用?

c# - WPF 强制 xaml 重新获取属性的值,尽管属性的 setter 没有更改

c# - 标签样式未在窗口状态更改时更新

c# - 带有网络服务的 MVVM

c# - OxyPlot InvalidatePlot 不刷新数据

entity-framework - 审核 Entity Framework 中的多对多关系?