2012-04-24 42 views
3

我對EF 4,外鍵和INotifyPropertyChanged /部分方法公開的標量屬性面臨一些困難。實體框架4 - 關於關聯的通知

我希望你能幫我找到正確的方法來做到這一點。

圖片我有一個與國家實體有* .. 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」發生,而不是「國家」。這使WPF中的數據綁定變得困難。

我的問題是:我該如何解決這個問題?

  • 我是否將我的對象包裝在ViewModel中,聽取屬性更改並去除「Key」部分?
  • 我是否修改T4模板?
  • 或者還有第三個選項我只是看不到?

我非常感謝這裏的任何建議,因爲我正在試驗WPF/EF而沒有在ViewModel中包裝每個Model屬性。

回答

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(); 
     } 
    } 
} 
+0

感謝您的回覆。我很難理解包裝每個實體的ViewModel中EF對象的好處。我試過這個的時候,我已經完成了一個ViewModel,它與模型中存在的屬性完全相同。也許我沒有正確使用MVVM。讓我細說一下:如果你要創建一個「CustomerScreen」,你會創建:OwnerViewView,OwnerWorkspaceViewModel,OwnerViewModel,OwnerEntity和OwnerRepository,或者OwnerWorkspaceViewModel和OwnerViewModel是否相同? – TroelsL 2012-04-25 10:57:26

+0

我已接受此答案。由於性能考慮,我沒有使用DynamicObjects,而是手動編碼DataItemViewModels。我已經得出結論:需要使用DataItemViewModel來撤消管理,計算屬性,並使應用程序獨立於OR映射器(EF)。再一次,謝謝。 – TroelsL 2012-04-30 11:37:58