O Entity Framework é um mecanismo de mapeamento objeto-relacional muito popular fornecido pela Microsoft e está sendo cada vez mais usado em aplicativos de alto tráfego. E muitos desses aplicativos de alto tráfego precisam de escalabilidade que vem usando um cache distribuído na memória. No entanto, em algumas situações, o Entity Framework e o cache distribuído na memória se tornam incompatíveis. Deixe-me explicar como.
Se você estiver usando o Entity Framework com POCO (Plain Old CLR Objects) juntamente com seu recurso de carregamento lento, o Entity Framework gera dinamicamente objetos proxy que contêm o código para fazer o carregamento lento. E essas definições de objetos dinâmicos só existem dentro do processo do aplicativo e, portanto, não podem ser serializadas para um cache distribuído fora do processo.
Aqui está um exemplo de proxy sendo gerado para um Customer
entidade onde você deseja usar o carregamento lento do Entity Framework para orders
. Veja como um nome de classe gerado dinamicamente aparece. Esse nome de classe só está disponível no processo de inscrição e seria desconhecido fora do processo.
Agora, o problema é que um cache distribuído está sempre fora de processo e exige que você serialize todos os objetos antes de armazená-los em cache e, em seguida, esses objetos podem precisar ser desserializados em uma máquina diferente quando são acessados de outra caixa de cliente. Portanto, o tipo de objeto deve ser conhecido em ambas as máquinas para fins de desserialização. E isso não é possível com o proxy gerado dinamicamente no Entity Framework porque eles são conhecidos apenas dentro do processo do aplicativo em uma máquina. Portanto, a única maneira para você é desabilitar a geração de proxy no Entity Framework para usá-lo com um cache distribuído, mas esse não é um cenário muito prático, pois isso também compromete os recursos de carregamento lento do Entity Framework.
Para superar esse problema e usar o Entity Framework com carregamento lento, primeiro você precisa desabilitar o proxy para que ele não cause problemas de serialização. Em seguida, você precisa injetar algum código adicional em seu aplicativo para obter o carregamento lento porque, se o proxy estiver desativado, não haverá suporte para carregamento lento no Entity Framework.
Além disso, você deve escrever esse código adicional de forma que ele ajude a obter a funcionalidade de carregamento lento do Entity Framework e, ao mesmo tempo, não viole o POCO-ness de suas entidades. Vamos discutir essa abordagem passo a passo em mais detalhes com um exemplo.
Se você tem um Customer
objeto que possui um Orders
list nele como uma propriedade, o carregamento lento deve funcionar de tal forma que, se você acessar o relacionado Orders
para um Cliente e eles ainda não foram carregados (o que significa que não existem no contexto do Entity Framework), então o Customer
objeto faz automaticamente uma chamada para carregar relacionado Orders
. Estou usando o exemplo abaixo para demonstrar como implementar o carregamento lento do Entity Framework sem proxy e também usá-los em um cache distribuído.
context.ContextOptions.ProxyCreationEnabled
propriedade é verdadeira por padrão. Você precisa defini-lo explicitamente como "false" no construtor padrão do seu objeto de contexto para desativar esse recurso.{
public NorthwindContext() : base("name=NorthwindContext")
{
this.ContextOptions.ProxyCreationEnabled = false;
this.ContextOptions.LazyLoadingEnabled = false;
}
}
Desabilitar a geração de proxy e as configurações de carregamento lento do Entity Framework garante que o proxy dinâmico não seja mais criado e que não haja nenhum problema de serialização com um cache distribuído. Objetos CLR antigos simples reais (POCO) Customer
object é usado pelo Entity framework e, portanto, pode ser armazenado em cache e recuperado em um cache distribuído sem problemas.Para isso, você cria outra classe que possui Entity framework estático ObjectContext
e uma função que é usada para a funcionalidade de carregamento lento do Entity Framework para pedidos. Essa função é chamada por referência no objeto POCO do cliente real posteriormente. Aqui está a definição de código para sua classe 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
objeto para que ele tenha a capacidade do Entity Framework de carregar lentamente todos os pedidos relacionados para um cliente. Aqui está como você atualiza o objeto Customer e os getters da lista de pedidos para chamar essa funcionalidade por referência. Você ainda precisa definir o OrderLazyLoad
método para apontar para a função de carregamento lento definida na classe Helper, que é feita na próxima etapa em seu programa principal. 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)
para ser o mesmo usado no programa principal para que todas as entidades sejam carregadas no mesmo contexto de objeto, ou seja, você está usando o mesmo contexto de objeto no programa consumidor, bem como na função definida na classe auxiliar para carregamento lento.NorthwindContext Context = new NorthwindContext();
CustomerHelper.CurrentContext = Context;
OrderLazyLoad
método para apontar para a função de carregamento lento do Helper. Este é o local mais apropriado para fazer isso, pois você não quer que seu Customer
object para se referir diretamente ao objeto da classe Helper.
Customer.OrderLazyLoad = Helper.CustomerHelper.DoLazyLoading;
Orders
a partir de um Customer
, a funcionalidade de carregamento lento induzido também é chamada.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();
}
Discuti uma abordagem para usar um cache distribuído para armazenar em cache Plain Old CLR Objects (POCO) usado no Entity Framework sem ter que se preocupar com o problema de serialização e ainda poder ter a funcionalidade completa do carregamento lento do Entity Framework. Embora essa abordagem envolva escrever algum código para obter carregamento lento, mas, em essência, você tem os benefícios do cache distribuído, como alto desempenho, escalabilidade e confiabilidade em um aplicativo de estrutura de entidade.