Сериализуемые классы и динамические прокси в EF - как? - PullRequest
19 голосов
/ 02 сентября 2011

В [предыдущее сообщение] , я был на пути к необходимости клонировать мои сущности.Это я пытался сделать с помощью подхода сериализации, который можно найти в [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

man.Мне нужно отверстие в голове.

* Обновление V *

Я не знаю, является ли проблема прокси или отложенной загрузкой, но после обдумываниянемного, кажется, что если я сделаю клон с помощью сериализации, все идентификаторы вещей, которые раньше принадлежали старому объекту, теперь будут принадлежать новому.Сначала я сделал .remove() на старом объекте, и если это немедленно сработает, возможно, в слежке есть что-то, что не знает об этом.Если этого не произойдет, то в один прекрасный момент появятся две вещи с одинаковым идентификатором ... поэтому я начинаю склоняться к идее @ Jockey об использовании инициализаторов объектов для клонирования ...

Ответы [ 4 ]

35 голосов
/ 02 сентября 2011

Если вы хотите сериализовать сущности, вы можете отключить создание прокси перед извлечением этого объекта. Вам также необходимо загрузить навигационные свойства, если вы хотите их сериализовать.

Чтобы отключить создание прокси в EF 4.1

dbContext.Configuration.ProxyCreationEnabled = false;

В EF 4

objectContext.ContextOptions.ProxyCreationEnabled = false;

например:

var users = context.Users.Include("Claims").Where(/**/);
1 голос
/ 02 сентября 2011

Отключите отложенную загрузку и отключите создание прокси-класса. В любом случае вам все равно нужно добавить атрибуты Serializable / DataContract, чтобы сделать его сериализуемым.

1 голос
/ 02 сентября 2011

Изучите шаблоны T4 для Entity Framework, вы можете контролировать, как EF генерирует ваши объекты, вам нужно будет определить, что они сериализуются в шаблоне T4.

0 голосов
/ 18 июля 2015

У меня была такая же проблема при использовании Entity Framework 6 (EF6). Я исправил проблему, изменив шаблон T4 и добавив строку [Serializable] между директивами Using и строкой открытия класса сущностей. Вот так:

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

Другие изменения не требуются.

...