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

在[之前的post]中 ,我被设置为必须克隆我的实体的路径。 我尝试使用[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.b__12() at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) InnerException: 

很明显,虽然我的类Claims是可序列化的,但EF生成的动态代理并不是……不知怎的,我的装饰品还没有流过。

这里的诀窍是什么?

*更新I *

更多上下文:我有一个类User ,其中包含一个属性Claims定义为ICollection 。 在进行克隆时,传递的类型是集合,而不是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 cs = new List(); 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_[...]并暗示我不仅需要使向下依赖可序列化,还有所有的路径备份! 但是,这样做我成功克隆了对象,但我现在回到原始post中的问题:

 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 

人。 我需要一个洞。

*更新V *

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

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

在EF 4.1中禁用代理创建

 dbContext.Configuration.ProxyCreationEnabled = false; 

在EF 4中

 objectContext.ContextOptions.ProxyCreationEnabled = false; 

例如:

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

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

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

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

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

无需其他更改。