Подтвердить что ты не робот

Сериализуемые классы и динамические прокси в EF - как?

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

Какой трюк здесь?

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

для большего контекста: у меня есть класс 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 *

ok, я изменил подход, реализуя конструктор следующим образом:

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 *

ok, поэтому проблема выше в том, что класс 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 *

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

4b9b3361

Ответ 1

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

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

dbContext.Configuration.ProxyCreationEnabled = false;

В EF 4

objectContext.ContextOptions.ProxyCreationEnabled = false;

например:

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

Никаких других изменений не требуется.

Ответ 5

Как рекомендация Microsoft, вам нужно использовать DTO вместо объектов EF, чтобы избежать проблем с сериализацией, подумайте также о том, чтобы использовать готовую загрузку перед преобразованием ваших объектов в DTO.

Одним из способов избежать проблем сериализации является сериализация объектов передачи данных (DTO) вместо объектов сущностей.

...

Что произойдет, если вы добавите соответствующее свойство навигации [...]?

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

Одним из решений является использование DTO, [...] В качестве альтернативы вы можете настроить JSON и XML-форматеры для обработки циклов графов.

EF Documentation - Как создать приложение Web API, которое использует Entity Framework для сохранения базы данных.