EF中的可序列化类和动态代理 - 如何?

时间:2011-09-01 21:03:00

标签: c# entity-framework asp.net-mvc-3 serialization clone

[a previous posting]中,我被设置为必须克隆我的实体的路径。我尝试使用[codeproject]中的序列化方法。

因为这些类是由Entity Framework生成的,所以我在自定义.cs中单独标记它们,如下所示:

[Serializable]
public partial class Claims
{
}

然而,当检查(在克隆方法中):

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

被击中,我收到错误:

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: 
很明显,虽然我的班级Claims是可序列化的,但EF生成的动态代理并不是......不知怎的,我的装饰品还没有流过。

这里的诀窍是什么?

*更新I *

更多上下文:我有一个类User,其中包含一个定义为Claims的属性ICollection<Claim>。在进行克隆时,传递的类型是集合,而不是Claim - 这解释了为什么克隆者抱怨该类型不可序列化。现在的问题是:如何将User.Claims序列化,因为我无法装饰属性?

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

*更新II *

练习的目的是设置一份深层副本。这就是它的样子:

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

为了让u.Claims.Clone()起作用,u.Claims必须是可序列化的,但不是出于上述原因。

*更新III *

好的,我改变了方法,实现了这样的构造函数:

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;

现在它已经过了clone()的检查(上面的“if”行),但现在它在:

formatter.Serialize(stream, source);

使用:

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
叹息......一切都那么难?

*更新IV *

好的,所以上面的问题是Claim类有一个指向User的导航器 - 这解释了为什么上面的方法表明类型为.User_[...]并暗示我不仅需要使向下依赖项可序列化,而且还需要备份所有路径!但是,完成后我成功克隆了对象,但我现在回到原始帖子中的问题:

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

人。我需要一个洞。

*更新V *

我不知道问题是代理还是延迟加载,但在考虑了一点之后,似乎如果我通过序列化进行克隆,那么过去属于旧对象的东西的所有ID现在将属于新的。我确实先在旧对象上做了.remove(),如果这有立竿见影的效果,那么跟踪中的某些内容可能并不知道。如果没有,那么在某一点上会有两个具有相同ID的东西......所以我开始倾向于@ Jockey使用对象初始化程序进行克隆的想法......

5 个答案:

答案 0 :(得分:35)

如果要序列化实体,可以在检索该对象之前禁用代理创建。如果您想要序列化导航属性,还需要加载导航属性。

在EF 4.1中禁用代理创建

dbContext.Configuration.ProxyCreationEnabled = false;

在EF 4中

objectContext.ContextOptions.ProxyCreationEnabled = false;

例如:

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

答案 1 :(得分:1)

查看实体框架的T4模板,您可以控制EF生成实体的方式,您必须在T4模板中定义它们是可序列化的。

答案 2 :(得分:1)

关闭延迟加载并关闭代理类创建。 无论如何,您仍然需要添加Serializable / DataContract属性以使其可序列化。

答案 3 :(得分:0)

我使用Entity Framework 6(EF6)时遇到了同样的问题。我通过更改T4模板并在Using Directives和Entity Class Opening行之间添加[Serializable]行修复了这个问题。像这样:

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

无需其他更改。

答案 4 :(得分:0)

作为Microsoft的建议,您需要使用DTO而不是EF实体来避免序列化问题,在将实体转换为DTO之前,还应考虑使用预先加载。

  

避免序列化问题的一种方法是序列化数据传输   对象(DTO)而不是实体对象。

     

...

     

如果添加相应的导航属性[...],会发生什么?

     

不幸的是,这在序列化模型时会产生问题。如果加载相关数据,它将创建一个圆形对象图。

     

一种解决方案是使用DTO,或者,您可以配置JSON和XML格式化程序来处理图形循环。

EF Documentation - How to create a Web API application that uses Entity Framework for database persistence.