3

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

我希望你能帮助我找到正确的方法来做到这一点。

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

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

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 中的数据绑定变得困难。

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

  • 我是否将我的对象包装在 ViewModel 中,监听属性更改并剥离“Key”部分?
  • 我要修改 T4 模板吗?
  • 还是有第三种我还看不到的选项?

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

4

1 回答 1

0

“最佳实践”方法是在视图模型中装饰您的模型,根据需要公开模型属性。你可以创建一个通用的 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();
        }
    }
}
于 2012-04-25T10:13:57.813 回答