L’approccio POCO di Entity Framework v4 permette di applicare la persistence-ignorance anche su oggetti custom attraverso la creazione a runtime (via Reflection.Emit) di proxy dinamici che li derivano in modo da permettere all’EF di implementarci sopra i suoi meccanismi di change tracking e lazy loading. In particolare, il change tracking è una feature che un oggetto proxy potrebbe continuare a mantenere anche quando attraversa tier diversi, il che ovviamente sposta l’attenzione verso problematiche legate alla serializzazione di oggetti il cui tipo effettivo non è disponibile a compile time.
Ad esempio, se volessimo serializzare e deserializzare su tier diversi un nostro oggetto Customer, il processo che si troverebbe a deserializzare il corrispondente tipo proxy autogenerato dall’EF dovrebbe essere in grado di riconoscere un oggetto di tipo...
System.Data.Entity.DynamicProxies.Customer_1941BFC82D29CB600101CF80564EA2CCAE83E1ACF5EF4B32E482FBDF13337DC9
definito nell’assembly (fittizio)
EntityFrameworkDynamicProxies-<NostroAssembly>, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
che ovviamente non è un “known type” nel suo AppDomain.
Ora, se utilizzassimo WCF come infrastruttura di comunicazione, non avremmo alcuna feature “gratis” per serializzare e deserializzare direttamente i tipi proxy di EF poiché il DataContractSerializer è in grado di gestire solo “known types” e purtroppo i proxy dinamici non lo sono. Quindi, le principali opzioni per aggirare il problema sono:
- Disabiltare la creazione dei proxy tramite la proprietà ProxyCreationEnabled (es. context.ContextOptions.ProxyCreationEnabled = false;) in modo da lavorare direttamente sui known types, ma perdendo ovviamente il change tracking automatico di EF.
- Usare la classe ProxyDataContractResolver per mappare i tipi proxy direttamente sui nostri tipi custom (esempio) che però a quel punto non supporterebbero più né il change tracking né tantomeno il lazy loading di EF. Soluzione sicuramente migliore per continuare a mantenere il change tracking su più tier è far riferimento a “self-tracking entities” ovvero ad oggetti che non hanno nessuna dipendenza con EF e definiscono internamente la loro logica di change tracking (ad esempio implementando l’interfaccia INotifyPropertyChanged).
Senza WCF c’è sempre la serializzazione “old-fashioned”. Ad esempio quella binaria. Affinché i tipi proxy siano resi disponibili alla deserializzazione binaria, EF ci espone un metodo CreateProxyTypes sull’oggetto ObjectContext per assicurare che i tipi proxy relativi ad un dato MetadataWorkspace siano deserializzabili all’interno di un AppDomain diverso da quello che invece li serializza. Occorre tenere a mente che la deserializzazione dei proxy ricostruisce degli oggetti che ovviamente non mantengono il loro comportamento originale (quindi niente change tracking).
Il seguente metodo, invocato ad esempio allo startup di una applicazione, è in grado di scatenare nell'AppDomain corrente la creazione di tutti i tipi proxy corrispondenti alle entity definite negli EDM (Entity Data Model) deployati come risorse all’interno di una lista di assembly, in modo che il BinaryFormatter sia poi in grado di trattare tali tipi come “known types”.
public static void CreateProxyTypes(Assembly[] assembliesToConsider)
{
var metadataWorkspace = new MetadataWorkspace(new string[] { "res://*/" }, assembliesToConsider);
using (var entityConnection = new EntityConnection(metadataWorkspace, new SqlConnection()))
{
using (var context = new ObjectContext(entityConnection))
{
foreach (var assembly in assembliesToConsider)
{
context.CreateProxyTypes(assembly.GetTypes());
}
}
}
}
Da notare come la classe EntityConnection richieda nel costruttore una connessione ad un data source che ai nostri fini però non è importante specificare. Per questo viene passata una SqlConnection “vuota”. Ciò che conta è referenziare gli EDM deployati come risorse all’interno degli assembly specificati. CreateProxyTypes è un metodo intelligente in quanto è in grado di ignorare tutti i tipi che non sono rappresentati negli EDM, quindi possiamo tranquillamente passargli come argomento tutti i tipi definiti all’interno di un assembly: esso internamente sarà in grado di controllare la sua cache di tipi e di trasformare in known types soltanto i tipi proxy di EF che ancora non sono stati creati. Per avere controprova dei tipi proxy correntemente registrati nel nostro AppDomain, basta chiamare il metodo statico ObjectContext.GetKnownProxyTypes().