[英]Entity Framework 4 and WPF
我正在編寫WPF應用程序,使用帶有Entity Framework 4作為ORM的MVVM設計。 我的視圖模型中具有集合屬性,該屬性將包含從EF4作為IEnumerable<T>
集合返回的實體的集合,以響應從業務層提交的查詢。
我曾希望將IEnumerable<T>
結果集簡單地包裝在ObservableCollection<T>
。 但是,我發現自己在存儲庫中編寫了更改跟蹤代碼,或者維護了更改對象的影子集合,只是為了使視圖模型和持久層保持同步。 每次將實體添加到視圖模型中的集合時,我都必須轉到存儲庫以將其添加到EF4 ObjectSet中。 我必須對更新和刪除執行相同的操作。
為簡化起見,我從CodePlex(http://waf.codeplex.com/)上的WPF應用程序框架項目中借用了EdmObservableCollection<T>
類。 該類使用對EF4 ObjectContext
的引用包裝ObservableCollection<T>
,以便可以在更新集合時更新OC。 我已經在下面重新打印了EdmObservableCollection
類。 該類工作得很好,但是它有點代碼味道,因為我最終在視圖模型中引用了EF4。
這是我的問題:在WPF應用程序中,使EF4實體集合與其對象上下文保持同步的通常方法是什么? EdmObservableCollection是合適的方法,還是有更好的方法? 我是否缺少使用EF4的基本知識? 謝謝你的幫助。
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Objects;
using System.Linq;
namespace Ef4Sqlce4Demo.ViewModel.BaseClasses
{
/// <summary>
/// An ObservableCollection for Entity Framework 4 entity collections.
/// </summary>
/// <typeparam name="T">The type of EF4 entity served.</typeparam>
/// <remarks>Developed from WPF Application Framework (WAF) http://waf.codeplex.com/</remarks>
public class EdmObservableCollection<T> : ObservableCollection<T>
{
#region Fields
// Member variables
private readonly string m_EntitySetName;
private readonly ObjectContext m_ObjectContext;
#endregion
#region Constructors
/// <summary>
/// Creates a new EDM Observable Collection and populates it with a list of items.
/// </summary>
/// <param name="objectContext">The EF4 ObjectContext that will manage the collection.</param>
/// <param name="entitySetName">The name of the entity set in the EDM.</param>
/// <param name="items">The items to be inserted into the collection.</param>
public EdmObservableCollection(ObjectContext objectContext, string entitySetName, IEnumerable<T> items)
: base(items ?? new T[] {})
{
if (objectContext == null)
{
throw new ArgumentNullException("objectContext");
}
if (entitySetName == null)
{
throw new ArgumentNullException("entitySetName");
}
m_ObjectContext = objectContext;
m_EntitySetName = entitySetName;
}
/// <summary>
/// Creates an empty EDM Observable Collection that has an ObjectContext.
/// </summary>
/// <param name="objectContext">The EF4 ObjectContext that will manage the collection.</param>
/// <param name="entitySetName">The name of the entity set in the EDM.</param>
public EdmObservableCollection(ObjectContext objectContext, string entitySetName)
: this(objectContext, entitySetName, null)
{
}
/// <summary>
/// Creates an empty EDM Observable Collection, with no ObjectContext.
/// </summary>
/// <remarks>
/// We use this constructor to create a placeholder collection before we have an
/// ObjectContext to work with. This state occurs when the program is first launched,
/// before a file is open. We need to initialize collections in the application's
/// ViewModels, so that the MainWindow can get Note and Tag counts, which are zero.
/// </remarks>
public EdmObservableCollection()
{
}
#endregion
#region Method Overrides
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
m_ObjectContext.AddObject(m_EntitySetName, item);
}
protected override void RemoveItem(int index)
{
T itemToDelete = this[index];
base.RemoveItem(index);
m_ObjectContext.DeleteObject(itemToDelete);
}
protected override void ClearItems()
{
T[] itemsToDelete = this.ToArray();
base.ClearItems();
foreach (T item in itemsToDelete)
{
m_ObjectContext.DeleteObject(item);
}
}
protected override void SetItem(int index, T item)
{
T itemToReplace = this[index];
base.SetItem(index, item);
m_ObjectContext.DeleteObject(itemToReplace);
m_ObjectContext.AddObject(m_EntitySetName, item);
}
#endregion
#region Public Methods
/// <summary>
/// Adds an object to the end of the collection.
/// </summary>
/// <param name="item">The object to be added to the end of the collection.</param>
public new void Add(T item)
{
InsertItem(Count, item);
}
/// <summary>
/// Removes all elements from the collection.
/// </summary>
/// <param name="clearFromContext">Whether the items should also be deleted from the ObjectContext.</param>
public void Clear(bool clearFromContext)
{
if (clearFromContext)
{
foreach (T item in Items)
{
m_ObjectContext.DeleteObject(item);
}
}
base.Clear();
}
/// <summary>
/// Inserts an element into the collection at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The object to insert.</param>
public new void Insert(int index, T item)
{
base.Insert(index, item);
m_ObjectContext.AddObject(m_EntitySetName, item);
}
/// <summary>
/// Updates the ObjectContext for changes to the collection.
/// </summary>
public void Refresh()
{
m_ObjectContext.SaveChanges();
}
/// <summary>
/// Removes the first occurrence of a specific object from the collection.
/// </summary>
/// <param name="item">The object to remove from the collection.</param>
public new void Remove(T item)
{
base.Remove(item);
m_ObjectContext.DeleteObject(item);
}
#endregion
}
}
我想我已經解決了。 問題不在於集合,而在於傳遞給集合的內容。 集合不應該直接與ObjectContext一起使用; 相反,它應針對其收集的實體類型與存儲庫一起使用。 因此,應該將Repository類傳遞給集合的構造函數,並且應該通過對Repository方法的簡單調用來替換集合中的所有持久性代碼。 修改后的收藏類如下所示:
編輯: Slauma詢問了有關數據驗證的問題(請參閱他的回答),所以我向我最初在答案中發布的集合類添加了CollectionChanging事件。 謝謝,Slauma,抓住了! 客戶端代碼應訂閱事件並使用它執行驗證。 將EventArgs.Cancel屬性設置為true以取消更改。
收集類
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Ef4Sqlce4Demo.Persistence.Interfaces;
using Ef4Sqlce4Demo.ViewModel.Utility;
namespace Ef4Sqlce4Demo.ViewModel.BaseClasses
{
/// <summary>
/// An ObservableCollection for Entity Framework 4 entity collections.
/// </summary>
/// <typeparam name="T">The type of EF4 entity served.</typeparam>
public class FsObservableCollection<T> : ObservableCollection<T> where T:class
{
#region Fields
// Member variables
private readonly IRepository<T> m_Repository;
#endregion
#region Constructors
/// <summary>
/// Creates a new FS Observable Collection and populates it with a list of items.
/// </summary>
/// <param name="items">The items to be inserted into the collection.</param>
/// <param name="repository">The Repository for type T.</param>
public FsObservableCollection(IEnumerable<T> items, IRepository<T> repository) : base(items ?? new T[] {})
{
/* The base class constructor call above uses the null-coalescing operator (the
* double-question mark) which specifies a default value if the value passed in
* is null. The base class constructor call passes a new empty array of type t,
* which has the same effect as calling the constructor with no parameters--
* a new, empty collection is created. */
if (repository == null) throw new ArgumentNullException("repository");
m_Repository = repository;
}
/// <summary>
/// Creates an empty FS Observable Collection, with a repository.
/// </summary>
/// <param name="repository">The Repository for type T.</param>
public FsObservableCollection(IRepository<T> repository) : base()
{
m_Repository = repository;
}
#endregion
#region Events
/// <summary>
/// Occurs before the collection changes, providing the opportunity to cancel the change.
/// </summary>
public event CollectionChangingEventHandler<T> CollectionChanging;
#endregion
#region Protected Method Overrides
/// <summary>
/// Inserts an element into the Collection at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The object to insert.</param>
protected override void InsertItem(int index, T item)
{
// Raise CollectionChanging event; exit if change cancelled
var newItems = new List<T>(new[] {item});
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add, null, newItems);
if (cancelled) return;
// Insert new item
base.InsertItem(index, item);
m_Repository.Add(item);
}
/// <summary>
/// Removes the item at the specified index of the collection.
/// </summary>
/// <param name="index">The zero-based index of the element to remove.</param>
protected override void RemoveItem(int index)
{
// Initialize
var itemToRemove = this[index];
// Raise CollectionChanging event; exit if change cancelled
var oldItems = new List<T>(new[] { itemToRemove });
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
if (cancelled) return;
// Remove new item
base.RemoveItem(index);
m_Repository.Delete(itemToRemove);
}
/// <summary>
/// Removes all items from the collection.
/// </summary>
protected override void ClearItems()
{
// Initialize
var itemsToDelete = this.ToArray();
// Raise CollectionChanging event; exit if change cancelled
var oldItems = new List<T>(itemsToDelete);
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
if (cancelled) return;
// Removes all items from the collection.
base.ClearItems();
foreach (var item in itemsToDelete)
{
m_Repository.Delete(item);
}
}
/// <summary>
/// Replaces the element at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the element to replace.</param>
/// <param name="newItem">The new value for the element at the specified index.</param>
protected override void SetItem(int index, T newItem)
{
// Initialize
var itemToReplace = this[index];
// Raise CollectionChanging event; exit if change cancelled
var oldItems = new List<T>(new[] { itemToReplace });
var newItems = new List<T>(new[] { newItem });
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Replace, oldItems, newItems);
if (cancelled) return;
// Rereplace item
base.SetItem(index, newItem);
m_Repository.Delete(itemToReplace);
m_Repository.Add(newItem);
}
#endregion
#region Public Method Overrides
/// <summary>
/// Adds an object to the end of the collection.
/// </summary>
/// <param name="item">The object to be added to the end of the collection.</param>
public new void Add(T item)
{
// Raise CollectionChanging event; exit if change cancelled
var newItems = new List<T>(new[] { item });
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add, null, newItems);
if (cancelled) return;
// Add new item
base.Add(item);
m_Repository.Add(item);
}
/// <summary>
/// Removes all elements from the collection and from the data store.
/// </summary>
public new void Clear()
{
/* We call the overload of this method with the 'clearFromDataStore'
* parameter, hard-coding its value as true. */
// Call overload with parameter
this.Clear(true);
}
/// <summary>
/// Removes all elements from the collection.
/// </summary>
/// <param name="clearFromDataStore">Whether the items should also be deleted from the data store.</param>
public void Clear(bool clearFromDataStore)
{
// Initialize
var itemsToDelete = this.ToArray();
// Raise CollectionChanging event; exit if change cancelled
var oldItems = new List<T>(itemsToDelete);
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
if (cancelled) return;
// Remove all items from the collection.
base.Clear();
// Exit if not removing from data store
if (!clearFromDataStore) return;
// Remove all items from the data store
foreach (var item in itemsToDelete)
{
m_Repository.Delete(item);
}
}
/// <summary>
/// Inserts an element into the collection at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The object to insert.</param>
public new void Insert(int index, T item)
{
// Raise CollectionChanging event; exit if change cancelled
var newItems = new List<T>(new[] { item });
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Add, null, newItems);
if (cancelled) return;
// Insert new item
base.Insert(index, item);
m_Repository.Add(item);
}
/// <summary>
/// Persists changes to the collection to the data store.
/// </summary>
public void PersistToDataStore()
{
m_Repository.SaveChanges();
}
/// <summary>
/// Removes the first occurrence of a specific object from the collection.
/// </summary>
/// <param name="itemToRemove">The object to remove from the collection.</param>
public new void Remove(T itemToRemove)
{
// Raise CollectionChanging event; exit if change cancelled
var oldItems = new List<T>(new[] { itemToRemove });
var cancelled = this.RaiseCollectionChangingEvent(NotifyCollectionChangingAction.Remove, oldItems, null);
if (cancelled) return;
// Remove target item
base.Remove(itemToRemove);
m_Repository.Delete(itemToRemove);
}
#endregion
#region Private Methods
/// <summary>
/// Raises the CollectionChanging event.
/// </summary>
/// <returns>True if a subscriber cancelled the change, false otherwise.</returns>
private bool RaiseCollectionChangingEvent(NotifyCollectionChangingAction action, IList<T> oldItems, IList<T> newItems)
{
// Exit if no subscribers
if (CollectionChanging == null) return false;
// Create event args
var e = new NotifyCollectionChangingEventArgs<T>(action, oldItems, newItems);
// Raise event
this.CollectionChanging(this, e);
/* Subscribers can set the Cancel property on the event args; the
* event args will reflect that change after the event is raised. */
// Set return value
return e.Cancel;
}
#endregion
}
}
事件Args類
using System;
using System.Collections.Generic;
namespace Ef4Sqlce4Demo.ViewModel.Utility
{
#region Enums
/// <summary>
/// Describes the action that caused a CollectionChanging event.
/// </summary>
public enum NotifyCollectionChangingAction { Add, Remove, Replace, Move, Reset }
#endregion
#region Delegates
/// <summary>
/// Occurs before an item is added, removed, changed, moved, or the entire list is refreshed.
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
/// <param name="sender">The object that raised the event.</param>
/// <param name="e">Information about the event.</param>
public delegate void CollectionChangingEventHandler<T>(object sender, NotifyCollectionChangingEventArgs<T> e);
#endregion
#region Event Args
public class NotifyCollectionChangingEventArgs<T> : EventArgs
{
#region Constructors
/// <summary>
/// Constructor with all arguments.
/// </summary>
/// <param name="action">The action that caused the event. </param>
/// <param name="oldItems">The list of items affected by a Replace, Remove, or Move action.</param>
/// <param name="newItems">The list of new items involved in the change.</param>
/// <param name="oldStartingIndex">The index at which a Move, Remove, or Replace action is occurring.</param>
/// <param name="newStartingIndex">The index at which the change is occurring.</param>
public NotifyCollectionChangingEventArgs(NotifyCollectionChangingAction action, IList<T> oldItems, IList<T> newItems, int oldStartingIndex, int newStartingIndex)
{
this.Action = action;
this.OldItems = oldItems;
this.NewItems = newItems;
this.OldStartingIndex = oldStartingIndex;
this.NewStartingIndex = newStartingIndex;
this.Cancel = false;
}
/// <summary>
/// Constructor that omits 'starting index' arguments.
/// </summary>
/// <param name="action">The action that caused the event. </param>
/// <param name="oldItems">The list of items affected by a Replace, Remove, or Move action.</param>
/// <param name="newItems">The list of new items involved in the change.</param>
public NotifyCollectionChangingEventArgs(NotifyCollectionChangingAction action, IList<T> oldItems, IList<T> newItems)
{
this.Action = action;
this.OldItems = oldItems;
this.NewItems = newItems;
this.OldStartingIndex = -1;
this.NewStartingIndex = -1;
this.Cancel = false;
}
#endregion
#region Properties
/// <summary>
/// Gets the action that caused the event.
/// </summary>
public NotifyCollectionChangingAction Action { get; private set; }
/// <summary>
/// Whether to cancel the pending change.
/// </summary>
/// <remarks>This property is set by an event subscriber. It enables
/// the subscriber to cancel the pending change.</remarks>
public bool Cancel { get; set; }
/// <summary>
/// Gets the list of new items involved in the change.
/// </summary>
public IList<T> NewItems { get; private set; }
/// <summary>
/// Gets the index at which the change is occurring.
/// </summary>
public int NewStartingIndex { get; set; }
/// <summary>
/// Gets the list of items affected by a Replace, Remove, or Move action.
/// </summary>
public IList<T> OldItems { get; private set; }
/// <summary>
/// Gets the index at which a Move, Remove, or Replace action is occurring.
/// </summary>
public int OldStartingIndex { get; set; }
#endregion
}
#endregion
}
我會提出一些想法,但沒有最終的答案。
我認為基本問題是:用戶是否可以始終以與數據庫操作相關的獨特方式對UI進行操作? 或更具體的說:如果用戶可以從UI上的列表中刪除項目或將新項目插入列表,這是否意味着必須從數據庫中刪除記錄或將記錄插入數據庫?
我認為答案是:否。
首先,我可以看到一個與EdmObservableCollection<T>
一起使用的好用例。 例如,這是WPF UI上僅具有綁定到客戶集合的DataGrid
的視圖。 客戶列表將通過查詢規范獲取。 現在,用戶可以在此DataGrid中進行編輯:他可以更改行(單個客戶),可以插入新行,也可以刪除行。 DataGrid非常輕松地支持這些操作,並且數據綁定引擎將這些“ CUD”操作直接寫入綁定的EdmObservableCollection。 在這種情況下,刪除行或插入新行實際上應該直接反映在數據庫上,因此EdmObservableCollection可能非常有用,因為它在內部處理ObjectContext中的插入和刪除。
但是即使在這種簡單情況下,也要考慮以下幾點:
無論如何,您可能仍需要將ObjectContext / Repository注入到ViewModel中(以查詢要放入集合中的對象),並且它必須與注入到EdmObservableCollection中的上下文相同,以處理對象更新(編輯客戶行)正確地。 如果您不想在調用SaveChanges之前進行手動的“后期”變更跟蹤,則還必須使用變更跟蹤對象/代理。
EdmObservableCollection<T>
提供的這種“通用”刪除操作不考慮數據庫或業務約束。 例如,如果用戶嘗試刪除分配給各種訂單的客戶的行,該怎么辦? 如果數據庫中存在外鍵關系,SaveChanges將失敗並引發異常。 好吧,您可能會捕獲此異常,對其進行評估並向用戶發送消息。 但是也許他做了許多其他更改(編輯了許多其他行並插入了新客戶),但是由於違反了FK約束,整個交易失敗了。 是的,您還可以處理(從ObjectContext中刪除此刪除的客戶,然后再次嘗試保存更改),甚至給客戶一個選擇的方式。 到目前為止,我們僅考慮了數據庫約束。 可能存在數據庫模型中未反映的其他業務規則(客戶在未支付所有發票之前無法刪除,刪除必須經過銷售部門老板的批准,客戶不得在之后的6個月內刪除他的最后命令,依此類推...)。 因此,除了簡單的“ ObjectContext.DeleteObject”以外,還涉及更多以安全和用戶友好的方式執行刪除的操作。
現在讓我們考慮另一個示例:假設有一個視圖可以將聯系人分配給訂單(嗯,可能很不尋常,但可以說它們是大型,復雜,非常單獨的訂單,其中包含很多客戶服務,每個訂單都需要不同的聯系人訂單各個方面的客戶網站)。 此視圖可能包含訂單的小型只讀視圖,已經在客戶的主數據中的聯系人池的只讀列表,然后是分配給訂單的聯系人的可編輯列表。 現在,就像第一個示例一樣,用戶可以執行類似的操作:他可以從列表中刪除聯系人,並且可以從主列表中拖放聯系人以將其插入到該訂單聯系人列表中。 如果我們再次將此列表綁定到EdmObservableCollection<T>
則會發生廢話:將新的聯系人插入數據庫,並將聯系人從數據庫中刪除。 我們不希望那樣,實際上我們只想分配或取消對現有記錄(客戶的聯系人主數據)的引用,而從不刪除或插入記錄。
因此,我們有兩個在UI上進行類似操作的示例(行已從列表中刪除並插入到列表中),但它們背后的業務規則完全不同,數據存儲區中的操作也不同。 對於WPF,會發生相同的情況(在兩種情況下都可以使用ObservableCollection進行處理),但是必須在業務和數據庫層中執行不同的操作。
我將從中得出一些結論:
當必須在UI上處理集合並且不必考慮困難的業務規則或數據庫約束時, EdmObservableCollection<T>
在特殊情況下會很有用。 但是它在許多情況下都不適用。 當然,您可以為其他情況創建派生的集合,這些情況會過載並以另一種方式實現例如Remove(T item)
(例如,不要從ObjectContext中刪除,而是將引用設置為null或其他內容)。 但是這種策略會將存儲庫或服務層的職責越來越多地轉移到那些專門的ObservableCollections中。 如果您的應用程序在DataGrid / List視圖中基本上執行類似於CRUD的操作,則EdmObservableCollection可能非常適合。 我還有其他疑問。
如前所述,在我看來,有更多的論點反對將數據庫/存儲庫操作與ObservableCollections的Insert / Remove操作耦合,因此反對使用類似EdmObservableCollection的構造。 我相信,在許多情況下,您的ViewModels將需要注入一個存儲庫或服務來滿足您的業務和數據庫層中的特定需求。 例如,對於刪除操作,您可以在ViewModel中有一個Command,然后在命令處理程序中執行以下操作:
private void DeleteCustomer(Customer customer) { Validator validator = customerService.Delete(customer); // customerService.Delete checks business rules, has access to repository // and checks also FK constraints before trying to delete if (validator.IsValid) observableCustomerCollection.RemoveItem(customer); else messageService.ShowMessage( "Dear User, you can't delete this customer because: " + validator.ReasonOfFailedValidation); }
在我看來,像這樣的復雜東西不屬於派生的ObservableCollection。
通常,我傾向於使工作單元盡可能的小-不是出於技術而是出於可用性的原因。 如果用戶在視圖中做了很多事情(編輯,刪除,插入等),並且在很多工作之后又單擊“保存”按鈕,那么很多事情也會出錯,他可能會一長串的驗證錯誤,並被迫糾正許多問題。 當然,在他可以完全按下“保存”按鈕之前,應該已經在UI中完成了基本驗證,但是更復雜的驗證將在稍后的業務層中進行。 例如,如果他刪除了一行,則像上面的示例一樣,我立即通過服務刪除(也許在確認消息框之后)。 插入內容也一樣。 由於我不使用變更跟蹤代理,因此更新可能會變得更加復雜(尤其是當涉及實體中的許多導航屬性時)。 (我不確定是否應該做得更好。)
我不希望使不同的事物看起來一樣。 為了分開關注,有一個CustomerService.Delete
和OrderContactPersonsService.Delete
是OrderContactPersonsService.Delete
,而ViewModels並不關心后面會發生什么。 但是在某些地方(業務層,存儲庫等),這些操作將有所不同,並且必須完成艱苦的工作。 具有內部IRepository的EdmObservableCollection對於從表示層到數據庫的整個鏈都是通用的,並試圖隱藏這些差異,這在除最簡單的CRUD應用程序之外的任何其他應用程序中都是不現實的。
在我看來,在EdmObservableCollection中使用ObjectContext / DbContext與IRepository相比是最小的問題。 EF上下文或ObjectSets / DbSets幾乎都是UnitOfWork / Repositories,當您應該更改數據庫訪問技術時是否不需要更改接口協定,這是個問題。 就我個人而言,我的通用存儲庫中有諸如“ Attach”或“ LoadNavigationCollection”之類的東西,對於我來說尚不清楚這些方法及其參數是否對另一個持久層有意義。 但是,使存儲庫變得更加抽象(希望擁有一個真正的Add-Update-Delete-Super-Persistance-Ignorant-Interface-Marvel<T>
)只會使它更趨於無用。 將EF抽象到IRepository中並不能解決我所描述的問題。
最后聲明:免責聲明:請謹慎閱讀我的話。 我不是一位經驗豐富的WPF / EF開發人員,我只是在研究將這兩種技術結合在一起的第一個更大的應用程序(大約兩個月以來)。 但是到目前為止,我的經驗是,我浪費了很多過於抽象的代碼減少嘗試。 如果出於維護原因和簡單起見,我會很高興,如果我能與EdmObservableCollection和只有一個通用存儲庫相處,但最終會有應用程序和客戶需求,不幸的是,它們需要許多不同的工作代碼。
如果需要,我可能會使用工廠模式在viewModel中實現抽象級別。
缺點是每次創建一個收藏集時,您都必須限制自己致電工廠。
因此,如果您的工廠有這樣的API(您可以使用所需的API進行切換):
public static class ObjectBuilder
{
static Factory;
SetFactory(IFactory factory) { Factory = factory; };
T CreateObject<T>() { return factory.Create<T>();};
TCollection<T> CreateObject<TCollection,T>>()
{
return Factory.Create<TCollection,T>();
}
TCollection<T> CreateObject<TCollection,T>>(TCollection<T> items)
{
return Factory.Create<TCollection,T>(TCollection<T> items);
}
}
所以現在您將:
IFactory
即可在TCollection is ObservableCollection
時返回您的EdmObservableCollection ObjectBuilder.SetFactory()
ObjectBuilder.Create<ObservableCollection,MyEntity>();
如果/當您需要更改ORM后端時,也只需實現一個新的IFactory
並調用ObjectBuilder.SetFactory(factory)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.