web-dev-qa-db-ja.com

EFのシリアル化可能なクラスと動的プロキシ-どのように?

[以前の投稿] で、エンティティのクローンを作成する必要があるという道にたどり着きました。 [codeproject] にあるように、これをシリアル化アプローチで試みました。

クラスはEntity Frameworkによって生成されるため、次のようにカスタム.csで個別にマークアップします。

_[Serializable]
public partial class Claims
{
}
_

ただし、(cloneメソッドで)チェックする場合:

_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クラスがあり、これには_ICollection<Claim>_として定義されたClaimsプロパティが含まれています。クローンを作成する場合、渡されるタイプはコレクションであり、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のものが2つあります...したがって、複製にオブジェクト初期化子を使用するという@Jockeyの考えに傾倒し始めています...

21
ekkis

エンティティをシリアル化する場合は、そのオブジェクトを取得する前にプロキシの作成を無効にできます。また、ナビゲーションプロパティもシリアル化する場合は、ナビゲーションプロパティを積極的にロードする必要があります。

EF 4.1でプロキシ作成を無効にするには

dbContext.Configuration.ProxyCreationEnabled = false;

EF 4で

objectContext.ContextOptions.ProxyCreationEnabled = false;

例えば:

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

遅延読み込みをオフにし、プロキシクラスの作成をオフにします。とにかく、シリアル化可能にするために、Serializable/DataContract属性を追加する必要があります。

1

Entity FrameworkのT4テンプレートを調べ、EFがエンティティを生成する方法を制御できます。T4テンプレートでシリアル化可能であることを定義する必要があります。

1
Cubicle.Jockey

Entity Framework 6(EF6)を使用して同じ問題が発生しました。 T4テンプレートを変更し、[ディレクティブを使用]と[エンティティクラスの開始]行の間に[Serializable]という行を追加して、問題を修正しました。そのようです:

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

その他の変更は必要ありません。

0
Wild Yaker

マイクロソフトの推奨事項として、シリアル化の問題を回避するには、EFエンティティの代わりにDTOを使用する必要があります。エンティティをDTOに変換する前に、積極的なロードを使用することも検討してください。

シリアル化の問題を回避する1つの方法は、エンティティオブジェクトではなくデータ転送オブジェクト(DTO)をシリアル化することです。

...

対応するナビゲーションプロパティ[...]を追加するとどうなりますか?

残念ながら、これはモデルをシリアル化するときに問題を引き起こします。関連データをロードすると、円形のオブジェクトグラフが作成されます。

1つの解決策は、DTOを使用することです。

EFドキュメント-データベースの永続化にEntity Frameworkを使用するWeb APIアプリケーションの作成方法。

0
Mselmi Ali