web-dev-qa-db-fra.com

Classes sérialisables et proxys dynamiques dans EF - comment?

Dans [une publication précédente] , j'étais sur le chemin de devoir cloner mes entités. J'ai tenté de le faire avec une approche de sérialisation comme celle trouvée dans [codeproject] .

parce que les classes sont générées par Entity Framework, je les marque séparément dans un fichier .cs personnalisé comme ceci:

[Serializable]
public partial class Claims
{
}

cependant, lorsque la vérification (dans la méthode de clonage):

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

est touché, je reçois l'erreur:

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: 

donc, apparemment, alors que ma classe Claims est sérialisable, les procurations dynamiques générées par EF ne le sont pas… d'une manière ou d'une autre, mes décorations ne circulent pas.

quel est le truc ici?

* Mise à jour I *

pour plus de contexte: j'ai une classe User qui contient une propriété Claims définie comme un ICollection<Claim>. lors du clonage, le type transmis est la collection, pas Claim - ceci explique pourquoi le cloneur se plaint du fait que le type n'est pas sérialisable. La question est donc de savoir comment rendre User.Claims sérialisable, car je ne peux pas décorer une propriété.

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

* Mise à jour II *

le but de l'exercice est de faciliter une copie en profondeur. voici à quoi ça ressemble:

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();
    }
}

pour que la u.Claims.Clone() fonctionne, u.Claims doit être sérialisable, mais ce n'est pas pour les raisons susmentionnées.

* Mise à jour III *

ok, j'ai changé d'approche, implémentant le constructeur comme ceci:

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;

et maintenant il passe la vérification de clone () (ligne "if" ci-dessus), mais maintenant, il se rompt à:

formatter.Serialize(stream, source);

avec:

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

soupir ... tout est-il toujours aussi dur?

* Mise à jour IV *

ok, le problème ci-dessus est que la classe Claim a un navigateur qui pointe sur User - ce qui explique pourquoi la méthode ci-dessus indique que le type est .User_[...] et implique que je dois non seulement rendre les dépendances descendantes sérialisables, mais également toutes les les chemins remontent! Cependant, après l'avoir fait, j'ai réussi à cloner l'objet, mais je reviens maintenant au problème dans mon message d'origine:

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

homme. J'ai besoin d'un trou dans la tête.

* Update V *

Je ne sais pas si le problème concerne les proxies ou le chargement paresseux, mais après y avoir un peu réfléchi, il semble que si je fais un clone via la sérialisation, tous les ID des éléments qui appartenaient à l'ancien objet disparaissent maintenant. appartenir au nouveau. J'ai d'abord fait une .remove() sur l'ancien objet et si cela a un effet immédiat, il se peut qu'il y ait quelque chose dans le suivi qui l'ignore. Si ce n'est pas le cas, à un moment donné, il y aura deux choses avec le même identifiant ... alors je commence à m'appuyer sur l'idée de @ Jockey d'utiliser des initialiseurs d'objet pour le clonage ...

18
ekkis

Si vous souhaitez sérialiser des entités, vous pouvez désactiver la création de proxy avant de récupérer cet objet. Vous devez également charger avec impatience les propriétés de navigation si vous souhaitez également les sérialiser.

Pour désactiver la création de proxy dans EF 4.1

dbContext.Configuration.ProxyCreationEnabled = false;

Dans EF 4

objectContext.ContextOptions.ProxyCreationEnabled = false;

par exemple:

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

Désactivez le chargement paresseux et la création de classes proxy. Quoi qu'il en soit, vous devez toujours ajouter les attributs Serializable/DataContract afin de le rendre sérialisable.

1
Marjan Nikolovski

Regardez dans les modèles T4 pour Entity Framework, vous pouvez contrôler la manière dont EF génère vos entités. Vous devrez alors définir qu'elles sont sérialisables dans le modèle T4.

1
Cubicle.Jockey

J'ai eu le même problème en utilisant Entity Framework 6 (EF6). J'ai résolu le problème en modifiant le modèle T4 et en ajoutant la ligne [Sérialisable] entre les lignes Utilisation des directives et Ouverture de la classe d'entité. Ainsi:

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

Aucun autre changement requis.

0
Wild Yaker