因此,我正在構建我的第一個更大的應用程序,並且我正在使用Windows的WPF和東西以及用於檢索,更新和存儲數據的實體框架。到目前爲止,使用類似於MVVM模式,我遇到了一些問題,但能夠解決它們,並且與設計相差甚遠。 另外,我正在使用數據庫第一種方法。數據綁定實體框架導航屬性 - 處理更改
但我剛碰到一堵我應該預料到的磚牆。它必須處理實體中的嵌套屬性,並處理對它們所做的更改。我們來解釋一下。
爲了簡單起見,我不會使用我的實際類名。 假設我的EF模型中有三個實體:Department,Manager和PersonalInfo。 我修改了我的* .tt模板文件,以便我的所有實體也實現INotifyPropertyChanged接口,但僅限於它們的NON NAVIGATION屬性,因爲導航屬性被聲明爲虛擬,並且在設置日期時將被EF重寫。
所以我們可以說我生成的類是這樣的:
public partial class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Department() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
int _someproperty;
public int SomeProperty { get { return _someproperty; } set { _someproperty= value; OnPropChange("SomeProperty"); } }
int _managerid;
public int ManagerID { get { return _managerid; } set { _managerid = value; OnPropChange("ManagerID"); } }
public virtual Manager Manager { get; set; }
}
public partial class Manager : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Manager() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
public virtual PersonalInfo PersonalInfo { get; set; }
}
public partial class PersonalInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public PersonalInfo() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
string _firstname;
public string FirstName { get { return _firstname; } set { _firstname = value; OnPropChange("FirstName"); } }
string _lastname;
public string LastName { get { return _lastname; } set { _lastname = value; OnPropChange("LastName"); } }
}
現在這個工作得很好,如果我想假設顯示器部門與他們的經理名單。首先,我將數據加載到EF背景下,像這樣
Context.Departments.Include(d => d.Manager.PersonalInfo).Load();
Departments = Context.Deparments.Local;
而不是在XAML我可以這樣做:
<DataGrid ItemsSource="{Binding Departments}" SelectedItem="{Binding CurrentDepartment, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}"/>SomeProperty
<DataGridTextColumn Binding="{Binding SomeProperty }" Header="Property"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.FirstName}" Header="FirstName"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.LastName}" Header="LastNameName"/>
</DataGrid.Columns>
</DataGrid>
而這一切奇妙的作品。我可以添加和刪除沒有問題的項目,只需從上下文中刪除它們並保存更改即可。由於實體集是ObservableCollections,所以添加或刪除它們會自動引發更新數據網格的適當事件。我還可以修改處任何nonnavigation財產,可以刷新數據CurrentDepartment像這樣:
Context.Entry(CurrentDepartment).Refresh();
,並在DataGrid自動刷新數據。
當我更改其中一個導航屬性時,就會出現問題。假設我打開了一個編輯部門的窗口,在那裏我將當前的經理從Bob Bobington改爲Dave Daveston。當我回到這個窗口電話:
Context.Entry(CurrentDepartment).Refresh();
它只會刷新非導航性能,以及第一和姓列仍將說鮑勃Bobington。但這是刷新功能按預期工作。但是,如果我正確的數據加載到這樣的背景下:
Context.Entry(CurrentDepartment).Reference(d=>d.Manager);
Context.Entry(CurrentDepartment.Manager).Reference(m=>m.PersonalInfo);
仍不會改變姓氏和名字列的內容,因爲它們仍結合老經理。只有在BobBinington實例的PersonalInfo發生變化時,它們纔會刷新。
我可以通過將列直接綁定到Manager屬性並通過ValueConverter或通過覆蓋管理器的ToString將Manager轉換爲文本來解決此問題。但這不會有幫助,因爲WPF永遠不會被通知管理器屬性已更改,因爲對該屬性的更改不會引發PropertyChanged事件。
導航性能無法提升該事件,因爲即使我編輯的TT模板,它的導航屬性,像這樣生成的代碼:
Manager _manager;
public virtual Manager Manager { get{return _manager;}
set{
_manager=value;
OnPropChange("Manager");
}
}
所有這些代碼可能會由EF框架本身覆蓋。
Sooo,在這些情況下最好的做法是什麼?請不要告訴我,傳統的看法是將EF Poco課程的數據複製到您自己的課程中並使用它們。 :(
UPDATE:。
這裏去了這個問題的潛在愚蠢的方案,但它的工作原理
ObservableCollection<Department> tempd = Departments;
Department temp = CurrentDepartment;
Departments = null;
CurrentDepartment = null;
Context.Entry(temp).Refresh();
Context.Entry(temp).Reference(d=>d.Manager).Load();
Context.Entry(temp.Manager).Reference(m=>m.PersonalInfo).Load();
Departments = tempd;
CurrentDepartment = temp;
正如你可以清楚地看到,關鍵是迫使在DataGrid重新綁定本身這種方式不會使用快捷方式,並且會重新綁定自己,但是這種方法非常愚蠢,我爲了不得不對數百個數據行執行此操作而發抖。我仍在等待一個合適的解決方案,但我會繼續使用它。有總比沒有好。
如果修改'ManagerId',調用'context.ChangeTracker.DetectChanges'應該保持屬性與'Manager'同步。 –
我不認爲這會有所幫助,因爲從我瞭解的DetectChanges檢查天氣實體已經改變,因爲上次數據庫調用/同步。它不會引發任何事件,也不會告訴您數據庫中有什麼變化。在管理器中保持正確的值並不是真正的問題,問題是如果Manager更改它,則不會引發WPF可以掛接的事件並知道它何時發生更改。 –
不,它不會引發事件,但會執行*關係修正*,這是EF確保外鍵和引用同步的內部過程。這將導致在處理引用時從數據庫中提取正確的管理器。 –