Entity Framework 是 Microsoft 提供的非常流行的对象关系映射引擎,并且越来越多地用于高流量应用程序。 而且,这些高流量应用程序中的许多都需要通过使用内存中分布式缓存来实现可扩展性。 但是,在某些情况下,Entity Framework 和内存中的分布式缓存变得不兼容。 让我解释一下。
如果您使用带有普通旧 CLR 对象 (POCO) 的实体框架及其延迟加载功能,实体框架会动态生成包含延迟加载代码的代理对象。 而且,这些动态对象定义只存在于应用程序进程中,因此不能为进程外分布式缓存进行序列化。
这是为 a 生成代理的示例 Customer
要使用实体框架延迟加载进行相关的实体 orders
. 查看动态生成的类名是如何出现的。 此类名称仅在应用程序进程内可用,并且在进程外是未知的。
现在,问题是分布式缓存总是不在进程中,并且需要您在缓存之前序列化所有对象,然后当从另一个客户端机器访问这些对象时,可能需要在不同的机器上对它们进行反序列化。 因此,为了反序列化目的,必须在两台机器上都知道对象类型。 这在 Entity Framework 中动态生成的代理是不可能的,因为它们只在一台机器上的应用程序进程中知道。 因此,您唯一的方法是在 Entity Framework 中禁用代理生成,以便将其与分布式缓存一起使用,但这不是一个非常实际的场景,因为这也会损害 Entity Framework 延迟加载功能。
为了克服这个问题并使用延迟加载的实体框架,您首先需要禁用代理,这样它就不会导致任何序列化问题。 然后,您需要在应用程序中注入一些额外的代码来实现自己的延迟加载,因为如果关闭代理,那么实体框架中不支持延迟加载。
此外,您应该编写此附加代码,以帮助您实现实体框架延迟加载功能,同时它不会违反实体的 POCO 特性。 让我们通过一个示例逐步详细讨论这种方法。
如果你有一个 Customer
对象有一个 Orders
将其作为属性列出,然后延迟加载应该以这样的方式工作:如果您访问相关的 Orders
对于客户并且它们尚未加载(意味着它们不存在于实体框架上下文中),那么 Customer
对象自动调用load相关 Orders
。我使用下面的示例来演示如何在没有代理的情况下实现实体框架延迟加载,并在分布式缓存中使用它们。
context.ContextOptions.ProxyCreationEnabled
属性默认为真。 您需要在上下文对象的默认构造函数中将其显式设置为“false”才能关闭此功能。{
public NorthwindContext() : base("name=NorthwindContext")
{
this.ContextOptions.ProxyCreationEnabled = false;
this.ContextOptions.LazyLoadingEnabled = false;
}
}
禁用代理生成和实体框架延迟加载设置可确保不再创建动态代理,并且分布式缓存不存在序列化问题。 实际普通旧 CLR 对象 (POCO) Customer
实体框架使用对象,因此可以在分布式缓存中缓存和检索它而不会出现任何问题。为此,您创建另一个具有实体框架静态的类 ObjectContext
以及一个用于实体框架延迟加载订单功能的函数。 稍后在实际的客户 POCO 对象中通过引用调用此函数。 这是您的 Helper 类的代码定义。
namespace Helper
{
public class CustomerHelper
{
public static NorthwindContext CurrentContext;
static public void DoLazyLoading(Customer customer, List<Order> orders)
{
if (CurrentContext == null) return; //no lazy loading
var query = from o in CurrentContext.Orders where o.CustomerID == customer.CustomerID select o;
foreach (Order o in query)
{ orders.Add(o); }
}
}
}
Customer
对象,以便它具有实体框架为客户延迟加载所有相关订单的能力。 以下是更新 Customer 对象和 Orders 列表获取器以通过引用调用此功能的方法。 您仍然需要设置 OrderLazyLoad
方法指向在 Helper 类中定义的延迟加载函数,该函数在主程序的下一步中完成。 namespace MyPOCOs
{
[Serializable]
public class Customer
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public static Action < Customer, List < Order >> OrderLazyLoad = null;
private List < Order > orders;
public virtual List< Order > Orders
{
get
{
if (orders == null)
{
orders = new List < Order >();
OrderLazyLoad(this, orders);
}
return orders;
}
set
{
orders = value;
}
}
}
[Serializable]
public class Order…
}
CustomerHelper.CurrentContext)
与主程序中使用的相同,以便所有实体都在相同的对象上下文下加载,即您在消费者程序中使用相同的对象上下文以及在帮助程序类中定义的函数中用于延迟加载。NorthwindContext Context = new NorthwindContext();
CustomerHelper.CurrentContext = Context;
OrderLazyLoad
方法指向Helper延迟加载函数。这是一个更合适的地方来执行此操作,因为您不希望您的 Customer
object 直接引用 Helper 类对象。
Customer.OrderLazyLoad = Helper.CustomerHelper.DoLazyLoading;
Orders
从 Customer
,诱导延迟加载功能也被调用。static void Main(string[] args)
{
Cache mycache = NCache.InitializeCache("mycache");
NorthwindContext Context = new NorthwindContext();
CustomerHelper.CurrentContext = Context;
Customer.OrderLazyLoad = Helper.CustomerHelper.DoLazyLoading;
var query = from c in Context.Customers where c.CompanyName.StartsWith("b") select c;
foreach (Customer c in query)
{
mycache.Insert(c.CustomerID, c);
Console.WriteLine("{0}", c.CustomerID);
foreach (Order order in c.Orders)
{
Console.WriteLine("\t{0}", order.OrderDate.ToString());
}
}
Console.ReadLine();
}
我已经讨论了一种使用分布式缓存来缓存实体框架中使用的普通旧 CLR 对象 (POCO) 的方法,而不必担心序列化问题,并且仍然能够拥有实体框架延迟加载的全部功能。 虽然,这种方法涉及编写一些代码来实现延迟加载,但本质上,您拥有分布式缓存的好处,例如实体框架应用程序中的高性能、可伸缩性和可靠性。