Clases serializables y proxies dynamics en EF: ¿cómo?

En [una publicación anterior] , estaba en el camino de tener que clonar mis entidades. Esto he intentado hacer con un enfoque de serialización como se encuentra en [codeproject] .

porque las clases son generadas por Entity Framework, las marco por separado en un .cs personalizado como este:

[Serializable] public partial class Claims { } 

Sin embargo, cuando el cheque (en el método de clonación):

 if (Object.ReferenceEquals(source, null)) { 

recibe un golpe, me sale el error:

 System.ArgumentException was unhandled by user code Message=The type must be serializable. Parameter name: source Source=Web ParamName=source StackTrace: at .Web.Cloner.Clone[T](T source) in C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Extensions.Object.cs:line 49 at .Web.Models.Employer..ctor(User u) in C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Models\EF.Custom.cs:line 121 at .Web.Controllers.AuthController.Register(String Company, String GivenName, String Surname, String Title, String Department) in C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Controllers\AuthController.cs:line 119 at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) at System.Web.Mvc.ControllerActionInvoker.c__DisplayClass15.b__12() at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) InnerException: 

Aparentemente, mientras que mi clase Claims es serializable, los proxies dynamics generados por EF no son … de alguna manera mis decoraciones no fluyen.

¿Cuál es el truco aquí?

* Actualizar I *

para obtener más contexto: tengo un User clase que contiene una propiedad Claims definidos como ICollection . Al hacer la clonación, el tipo que se pasa es la colección, no Claim : esto explica por qué el clonador se queja de que el tipo no es serializable. entonces la pregunta ahora es: ¿cómo puedo hacer User.Claims serializable ya que no puedo decorar una propiedad?

 Error 1 Attribute 'Serializable' is not valid on this declaration type. It is only valid on 'class, struct, enum, delegate' declarations. C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Models\EF.Custom.cs 128 10 Website 

* Actualización II *

el objective del ejercicio es instalar una copia profunda. Esto es lo que parece:

 public partial class Employer { public Employer(User u) { this.Id = u.Id; this.GivenName = u.GivenName; this.Surname = u.Surname; this.Claims = u.Claims.Clone(); this.Contacts = u.Contacts.Clone(); } } 

para que u.Claims.Clone() funcione, u.Claims debe ser serializable, pero no es por los motivos citados anteriormente.

* Actualización III *

Ok, cambié el enfoque, implementando el constructor de esta manera:

 public partial class Employer { public Employer(User u) { this.Id = u.Id; this.GivenName = u.GivenName; this.Surname = u.Surname; ICollection cs = new List(); foreach (Claim c in u.Claims) { cs.Add(c.Clone()); } this.Claims = cs; 

y ahora pasa el control de clon () (línea “si” arriba), pero ahora se rompe en:

 formatter.Serialize(stream, source); 

con:

 System.Runtime.Serialization.SerializationException was unhandled by user code Message=Type 'System.Data.Entity.DynamicProxies.User_7B7AFFFE306AB2E39C07D91CC157792F503F36DFCAB490FB3333A52EA1D5DC0D' in Assembly 'EntityFrameworkDynamicProxies-Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable. Source=mscorlib StackTrace: at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder) at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo) at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph) at Skillscore.Web.Cloner.Clone[T](T source) in C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Extensions.Object.cs:line 62 at Skillscore.Web.Models.Employer..ctor(User u) in C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Models\EF.Custom.cs:line 130 

Suspiro … ¿todo es siempre tan difícil?

* Actualización IV *

ok, entonces el problema anterior es que la clase Claim tiene un navegador que apunta al User , lo que explica por qué el método anterior indica que el tipo es .User_[...] e implica que necesito no solo hacer las dependencias descendentes serializables , pero también todos los caminos de regreso! Sin embargo, una vez hecho esto, cloné el objeto con éxito, pero ahora vuelvo al tema en mi publicación original:

 System.InvalidOperationException was unhandled by user code Message=Conflicting changes to the role 'User' of the relationship 'EF.ClaimUser' have been detected. Source=System.Data.Entity StackTrace: at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach) at System.Data.Objects.DataClasses.EntityCollection`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach) at System.Data.Objects.DataClasses.RelationshipManager.AddRelatedEntitiesToObjectStateManager(Boolean doAttach) at System.Data.Objects.ObjectContext.AddObject(String entitySetName, Object entity) at System.Data.Entity.Internal.Linq.InternalSet`1.c__DisplayClass5.b__4() at System.Data.Entity.Internal.Linq.InternalSet`1.ActOnSet(Action action, EntityState newState, Object entity, String methodName) at System.Data.Entity.Internal.Linq.InternalSet`1.Add(Object entity) at System.Data.Entity.DbSet`1.Add(TEntity entity) at Skillscore.Web.Controllers.AuthController.Register(String Company, String GivenName, String Surname, String Title, String Department) in C:\Users\.\Documents\Visual Studio 2010\Projects\.\Website\Controllers\AuthController.cs:line 138 

hombre. Necesito un agujero en la cabeza.

* Actualización V *

No sé si el problema son los proxies o la carga diferida, pero después de pensarlo un poco, parece que si hago un clon por serialización, todos los ID de las cosas que solían pertenecer al antiguo objeto ahora van pertenecer al nuevo. Hice un .remove() primero en el objeto viejo y si eso tiene efecto inmediato, entonces tal vez hay algo en el seguimiento que no conoce. Si no lo hace, en un momento habrá dos cosas con la misma ID … así que estoy empezando a inclinarme hacia la idea de @Jockey de usar los inicializadores de objetos para la clonación …

Si desea serializar entidades, puede desactivar la creación del proxy antes de recuperar ese objeto. También necesita cargar propiedades de navegación de carga si desea serializarlas también.

Para deshabilitar la creación de proxy en EF 4.1

 dbContext.Configuration.ProxyCreationEnabled = false; 

En EF 4

 objectContext.ContextOptions.ProxyCreationEnabled = false; 

p.ej:

 var users = context.Users.Include("Claims").Where(/**/); 

Consulte las plantillas T4 para Entity Framework, puede controlar cómo EF genera sus entidades, deberá definir que son serializables en la Plantilla T4.

Desactiva la carga diferida y desactiva la creación de la clase proxy. De todos modos, aún necesita agregar los atributos de Serializable / DataContract para que sea serializable.

Tuve el mismo problema al usar Entity Framework 6 (EF6). Solucioné el problema cambiando la plantilla T4 y agregando la línea [Serializable] entre las Directivas de uso y la Línea de apertura de la Clase de entidad. Al igual que:

  <#=codeStringGenerator.UsingDirectives(inHeader: false)#> [Serializable] <#=codeStringGenerator.EntityClassOpening(entity)#> 

No se requieren otros cambios.