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

发布于 2024-12-02 19:34:54 字数 7228 浏览 2 评论 0原文

[之前的帖子]中,我决定必须克隆我的实体。我尝试使用 [codeproject]

因为这些类是由实体框架生成的,所以我在自定义 .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: 

所以显然,而我的类 声明 是可序列化的,EF 生成的动态代理不是......不知怎的,我的装饰没有流过。

这里有什么窍门?

* 更新 I *

了解更多上下文:我有一个类 User,其中包含定义为 ICollection< 的属性 Claims /代码>。在进行克隆时,传递的类型是集合,而不是声明 - 这解释了为什么克隆者抱怨该类型不可序列化。所以现在的问题是:由于我无法装饰属性,如何使 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

man.我需要在头上打一个洞。

* 更新 V *

我不知道问题是代理还是延迟加载,但稍微想了一下,似乎如果我通过序列化进行克隆,所有事物的 ID曾经属于旧对象的内容现在将属于新对象。我确实首先对旧对象执行了 .remove() ,如果这立即生效,那么跟踪中可能有一些不知道的东西。如果没有,那么在某一时刻将会有两个具有相同ID的东西......所以我开始倾向于@Jockey使用对象初始化器进行克隆的想法......

In [a previous posting], I was set on the path to having to clone my entities. This I've attempted to do with a serialisation approach as found in [codeproject].

because the classes are generated by Entity Framework, I mark them up separately in a custom .cs like this:

[Serializable]
public partial class Claims
{
}

however, when the check (in the clone method):

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

gets hit, I get the 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 apparently whilst my class Claims is serialisable, the dynamic proxies generated by EF are not... somehow my decorations are not flowing through.

what's the trick here?

* Update I *

for more context: I have a class User which contains a property Claims defined as an ICollection<Claim>. when doing the cloning, the type that gets passed is the collection, not Claim - this explains why the cloner is complaining that the type is not serializable. so the question now is: how do I make User.Claims serializable since I can't decorate a property?

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

* Update II *

the point of the exercise is to facility a deep copy. this is what it looks like:

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

in order for the u.Claims.Clone() to work, u.Claims must be serializable but it's not for the reasons cited above.

* Update III *

ok, I changed approach, implementing the constructor like this:

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;

and now it gets past the clone()'s check ("if" line above), but now it breaks at:

formatter.Serialize(stream, source);

with:

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

sigh... is everything always so hard?

* Update IV *

ok, so the problem above is that the Claim class has a navigator that points back to User - which explains why the above method indicates the type to be .User_[...] and implies I need to not only make the downward dependencies serializable, but also all of the paths back up! However, having done that I successfully clone the object but I'm now back to the issue in my original posting:

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

man. I need a hole in the head.

* Update V *

I don't know if the issue is the proxies or lazy loading, but after thinking about it a little, it seems that if I do a clone via serialisation, all the IDs for things that used to belong to the old object are now going to belong to the new one. I did do a .remove() first on the old object and if that has immediate effect then maybe there's something in the tracking that doesn't know about it. If it doesn't, then at one point there will be two things out there with the same ID... so I'm starting to lean towards @Jockey's idea of using object initialisers for the cloning...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不一样的天空 2024-12-09 19:34:54

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

在 EF 4.1 中禁用代理创建

dbContext.Configuration.ProxyCreationEnabled = false;

在 EF 4 中,

objectContext.ContextOptions.ProxyCreationEnabled = false;

例如:

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

If you want to serialize entities you can disable proxy creation before retrieving that object. You also need to eager load navigational properties if you want to serialize them as well.

To disable proxy creation in EF 4.1

dbContext.Configuration.ProxyCreationEnabled = false;

In EF 4

objectContext.ContextOptions.ProxyCreationEnabled = false;

eg:

var users = context.Users.Include("Claims").Where(/**/);
娇纵 2024-12-09 19:34:54

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

Look into T4 Templates for Entity Framework, you can control how EF generates your entities, you will have to define that they are serializable in the T4 Template.

执手闯天涯 2024-12-09 19:34:54

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

Turn off lazy loading and turn off proxy class creation.
Anyway you still need to add the Serializable/DataContract attributes in order to make it serializable.

深爱不及久伴 2024-12-09 19:34:54

我在使用实体框架 6 (EF6) 时遇到了同样的问题。我通过更改 T4 模板并在使用指令和实体类开头行之间添加行 [Serialized] 解决了该问题。像这样:

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

不需要其他更改。

I had the same issue using Entity Framework 6 ( EF6 ). I fixed the issue by changing the T4 template and adding the line [Serializable] between the Using Directives and the Entity Class Opening line. Like so:

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

No other changes required.

不再见 2024-12-09 19:34:54

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

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

...

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

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

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

EF 文档 - 如何创建使用实体框架进行数据库持久化的 Web API 应用程序。

As a Microsoft recommendation, you need to use DTO instead of EF entities to avoid serialization issues, consider also to use eager loading before transforming your entities to DTO.

One way to avoid serialization problems is to serialize data transfer
objects (DTOs) instead of entity objects.

...

What happens if you add the corresponding navigation property [...]?

Unfortunately, this creates a problem when you serialize the models. If you load the related data, it creates a circular object graph.

One solution is to use DTOs, [...] Alternatively, you can configure the JSON and XML formatters to handle graph cycles.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文