Сериализуемая сущность .NET

Мне нужно сделать все мои сущности сериализуемыми. Итак, я думал о BaseEntity с методом резервного копирования и восстановления. Но при восстановлении я не могу заменить объект сохраненным, потому что this доступен только для чтения.

Любое решение или какой-либо другой способ получить сериализуемые объекты?

Мой код:

internal class BaseEntity
{
    private MemoryStream ms = new MemoryStream();
    private BinaryFormatter bf = new BinaryFormatter();

    public void Backup()
    {
        bf.Serialize(ms, this);
    }

    public void Restore()
    {
        this = (BaseEntity)bf.Deserialize(ms);
    }
}

person Diego    schedule 14.06.2011    source источник
comment
мелочь. Я бы переименовал ваш класс, в будущем это будет сбивать с толку не только вас, но и других, кто придет работать над кодом. Я сразу предположил, что это интерфейс.   -  person Mike Miller    schedule 15.06.2011
comment
@Mike Miller: вы правы, извините, я начал с интерфейса, а затем изменил его. забыл переименовать. Спасибо!!   -  person Diego    schedule 15.06.2011
comment
(стоит ли мне вообще возиться с моей обычной анти-BinaryFormatter тирадой?)   -  person Marc Gravell    schedule 15.06.2011
comment
@Marc Gravell: не могли бы вы дать несколько ссылок, почему бы не использовать BinaryFormatter и как его заменить?   -  person Diego    schedule 15.06.2011
comment
@Marc Gravell: Спасибо !! Я буду использовать ваш ответ как ссылку на эту тему   -  person Diego    schedule 15.06.2011


Ответы (4)


Изменить: один из способов сериализации - использовать System.Runtime.Serialization.Formatters.Binary.BinaryFormatter (или другую реализацию IFormatter). Чтобы сериализовать объект, вы передаете объект и поток. Чтобы десериализовать объект, вы передаете поток (расположенный в начале сериализованных данных), и он возвращает сериализованный объект и все его зависимости.

public static class EntityBackupServices
{
   public static MemoryStream Backup (BaseEntity entity)
   {
      var ms = new MemoryStream();
      Serialize (ms, entity);
      ms.Position = 0;
      return ms;
   }
   public static void Serialize (Stream stream, BaseEntity entity)
   {
      var binaryFormatter = new BinaryFormatter();
      binaryFormatter.Serialize (stream, entity);
   }
   public static BaseEntity Restore (Stream stream)
   {
      var binaryFormatter = new BinaryFormatter();
      var entity = (BaseEntity) binaryFormatter.Deserialize (stream);
      return entity;
   }
}

Одно средство форматирования не выполняет (хотя класс FormatterServices делает это возможным) - это изменять существующие объекты. Поэтому вы, вероятно, не хотите иметь метод экземпляра под названием Deserialize. Вы действительно не можете этого сделать: new LionEntity().Deserialize () где он заменяет поля существующего экземпляра.

Примечание. Вам нужно будет поместить Serializable по всем вашим типам. Любые поля, которые нельзя сериализовать (потому что это либо не структура, либо не помечено как [Serializable], необходимо пометить NonSerialized.

// A test object that needs to be serialized.
[Serializable()]        
public class BaseEntity
{
    public int member1;
    public string member2;
    public string member3;
    public double member4;

    // A field that is not serialized.
    [NonSerialized()] public MyRuntimeType memberThatIsNotSerializable; 

    public TestSimpleObject()
    {
        member1 = 11;
        member2 = "hello";
        member3 = "hello";
        member4 = 3.14159265;
        memberThatIsNotSerializable = new Form ();
    }

    public MemoryStream Backup ()
    {
       return EntityBackupServices.Backup (this);
    }
}

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

public class BaseEntity
{
   void Restore(Stream stream)
   {
      object deserialized = EntityBackupServices.RestoreDeserialize(stream);//As listed above
      if (deserialized.GetType () != this.GetType ())
         throw new Exception();
      foreach (FieldInfo fi in GetType().GetFields())
      {
         fi.SetValue(this, fi.GetValue (deserialized));
      }
   }
}
person agent-j    schedule 14.06.2011
comment
И все же, как бы вы провели сериализацию, не понимая свойств класса? (Чтобы его можно было переместить в суперкласс). - person Diego; 15.06.2011
comment
В вашем примере показан BinaryFormatter (я полагаю, из System.Runtime.Serialization.Formatters.Binary). BinaryFormatter способен десериализовать любую структуру и любой класс, помеченный как [Serializable]. Для этого вы вызываете binaryFormatter.Deserialize (myStream). Я обновлю свой ответ, чтобы показать, как базовый класс должен / не должен / делать это. - person agent-j; 15.06.2011
comment
В вашем примере методы являются статическими и возвращают / получают некоторую сущность. Я также могу сделать это в моем классе BaseEntity, проблема начинается, когда я хочу сделать это так просто, как: entity.Backup() и entity.Restore() - person Diego; 15.06.2011
comment
Я отредактировал свой пост, чтобы предложить другое решение, более соответствующее вашим желаниям. - person agent-j; 15.06.2011

Более распространенный шаблон - не возлагать на объекты ответственность за сериализацию / десериализацию самих себя; скорее используйте внешний сериализатор:

var serializer = new DataContractJsonSerializer(typeof(YourClass));
var stream = ...;
YourClass yourObj = ...;

serializer.WriteObject(stream, yourObj);

var restoredObj = serializer.ReadObject(stream);
person Jacob    schedule 14.06.2011
comment
+1 - этот подход мне больше понятен. хотя и не отвечая напрямую на вопрос, он добавляет ценности, предлагая альтернативу (о которой просили). - person Mike Miller; 15.06.2011
comment
Это решение может быть лучше в некоторых отношениях, но это не то, что мне нужно. Спасибо, в любом случае! - person Diego; 15.06.2011

public IEntidadBase Restore()
{
    return (IEntidadBase)bf.Deserialize(ms);
}

@jacklondon, как бы вы использовали методы EntitySerializer?

Вы можете выполнить процесс сериализации с помощью модуля http://www.servicestack.net/ StackService.Text для чистых объектов. Вам не нужен какой-либо атрибут (сериализуемый / datacontract) в формате ms.

 public class EntityFoo
    {
        public string Bar { get; set; }

        public EntityFoo (string bar)
        {
            Bar = bar;
        }
    }

    public class EntityDumper //and the EntitySerializer
    {
        public static string Dump<T> (T entity)
        {
            return new TypeSerializer<T> ().SerializeToString (entity);
        }

        public static T LoadBack<T> (string dump)
        {
            return new TypeSerializer<T> ().DeserializeFromString (dump);
        }
    }



    public class dump_usage
    {
        public void start ()
        {
            string dump = EntityDumper.Dump (new EntityFoo ("Space"));

            EntityFoo loaded = EntityDumper.LoadBack<EntityFoo> (dump);
            Debug.Assert (loaded.Bar == "Space");
        }
    }
person jackie    schedule 14.06.2011
comment
Разве он не должен быть статичным, чтобы быть полезным? - person Jouke van der Maas; 15.06.2011
comment
@jacklondon: Да, это могло бы быть решением, но я бы предпочел (если возможно), чтобы сериализация содержалась в сущности. Таким образом, извне класса сущности я должен был бы сделать: myEntity = myEntity.Restore();, и это не очень хорошо. - person Diego; 15.06.2011
comment
Если каждая сущность сериализует себя, этот метод не может быть статическим, я ошибаюсь? - person Diego; 15.06.2011
comment
Я думаю, что создание EntitySerializer было бы лучшим решением. Вы возлагаете на свою сущность слишком много ответственности. Это незаконно для srp. - person jackie; 15.06.2011
comment
EntitySerializer? есть ссылка на msdn? - person Mike Miller; 15.06.2011
comment
У Microsoft есть два сериализатора xmlserializer (и новый datacontractserializer), binaryserializer. msdn.microsoft.com/en-us/library/, msdn.microsoft.com/en -us / библиотека /. EntitySerializer; Я уже упоминал, что это специальный класс, который инкапсулирует процесс сериализации. - person jackie; 15.06.2011
comment
@jacklondon, как бы вы использовали методы EntitySerializer? - person Diego; 15.06.2011

Я не обязательно рекомендую это, но вот один шаблон для объекта, который может сохраняться и восстанавливать свое собственное состояние с помощью сериализации, которая создает новые экземпляры:

public sealed class MyClass
{
    private Data _data = new Data();

    //Properties go here (access the public fields on _data)

    public void Backup()
    {
        //Serialize Data
    }

    public void Restore()
    {
        //Deserialize Data and set new instance
    }

    private sealed class Data
    {
        //Public fields go here (they're private externally [because Data is private], but public to MyClass.)
    }
}

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

person Dan Bryant    schedule 14.06.2011
comment
Пожалуйста, поправьте меня, если я ошибаюсь, но я думаю, что ваша модель предполагает, что я сохраняю свойство за свойством в данные. Я хотел бы иметь суперкласс, который предоставляет всем своим дочерним классам методы, используемые для сериализации, поэтому этому суперклассу не нужно ничего знать о сущности, которую он сериализует. - person Diego; 15.06.2011
comment
@Diego, правильно, это строго один из способов разрешить десериализацию для конкретного существующего экземпляра, но с использованием систем сериализации, которые создают новые экземпляры. На самом деле я не рекомендую делать это (я думаю, что гораздо разумнее, чтобы за сериализацию отвечал другой класс), но это шаблон, который выполняет часть того, что вам нужно. - person Dan Bryant; 15.06.2011