Clases serializables y proxies dinámicos en EF: ¿cómo?
In [una publicación anterior], Estaba en el camino para tener que clonar mis entidades. Esto he intentado hacer con un enfoque de serialización como se encuentra en [proyecto de código].
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 la verificación (en el método de clonación):
if (Object.ReferenceEquals(source, null))
{
gets hit, 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.<InvokeActionMethodWithFilters>b__12()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
InnerException:
so aparentemente mientras mi claseClaims
es serializable, los proxys dinámicos generados por EF no lo son ... de alguna manera mis decoraciones no fluyen.
¿Cuál es el truco aquí?
* Actualización I *
para más contexto: tengo una claseUser
que contiene una propiedadClaims
definido como unICollection<Claim>
. al hacer la clonación, el tipo que se pasa es la colección, noClaim
: esto explica por qué el clonador se queja de que el tipo no es serializable. así que la pregunta ahora es: ¿cómo hagoUser.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 *
l objetivo del ejercicio es facilitar 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();
}
}
en orden para lau.Claims.Clone()
trabajar,u.Claims
debe ser serializable, pero no por las razones citadas anteriormente.
* Actualización III *
ok, cambié el enfoque, implementando el constructor así:
public partial class Employer
{
public Employer(User u)
{
this.Id = u.Id;
this.GivenName = u.GivenName;
this.Surname = u.Surname;
ICollection<Claim> cs = new List<Claim>();
foreach (Claim c in u.Claims)
{
cs.Add(c.Clone());
}
this.Claims = cs;
y ahora pasa la comprobación del clon () (línea "if" 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 elClaim
class tiene un navegador que apunta de nuevo aUser
- lo que explica por qué el método anterior indica el tipo que se.User_[...]
e implica que no solo necesito hacer que las dependencias descendentes sean serializables, ¡sino que también todas las rutas vuelven a funcionar! Sin embargo, una vez hecho esto, cloné el objeto con éxito, pero ahora vuelvo al problema 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.<Add>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.
* Actualizar 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 a través de la serialización, todas las ID de las cosas que solían pertenecer al objeto anterior ahora son va a pertenecer al nuevo. Hice un.remove()
primero en el objeto antiguo y si eso tiene un efecto inmediato, entonces tal vez haya algo en el seguimiento que no lo sepa. Si no es así, en un momento habrá dos cosas con la misma ID ... así que estoy empezando a inclinarme hacia la idea de @ Jockey de usar iniciadores de objetos para la clonación ...