[英]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.