【问题标题】:.NET Serializable entity.NET 可序列化实体
【发布时间】:2011-06-14 20:59:24
【问题描述】:

我需要使我的所有实体都可序列化。所以我在想一个带有备份和恢复方法的 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);
    }
}

【问题讨论】:

  • 小点我会重命名你的类,它会在未来不仅对你而且对其他来处理代码的人来说都是混乱的。我立即认为这是一个界面。
  • @Mike Miller:你说得对,对不起,我从一个界面开始,然后改变了它。忘记重命名了。谢谢!!
  • (我是否应该为我一贯的反BinaryFormatter 咆哮而烦恼?)
  • @Marc Gravell:您能否提供一些为什么不使用 BinaryFormatter 以及如何替换它的链接?
  • @Diego 当然,see this answer。我可能有偏见,但我使用 protobuf-net(好吧,我也写过),它解决了所有这些问题。

标签: c# .net serialization entities


【解决方案1】:

更常见的模式是不让对象自行序列化/反序列化;而是使用外部序列化程序:

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

serializer.WriteObject(stream, yourObj);

var restoredObj = serializer.ReadObject(stream);

【讨论】:

  • +1 - 这种方法对我来说更有意义。虽然没有直接回答这个问题,但它通过提供替代方案(被要求)来增加价值。
  • 这个解决方案在某些方面可能更好,但它不是我需要的。还是谢谢!
【解决方案2】:

编辑:序列化的一种工作方式是使用 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);
    }
}

编辑: 我提到的方式是一种相当标准且被接受的方式。如果您想冒险进入 hackdom,您可以按照我提到的方式反序列化对象,然后使用反射将现有对象上的每个字段设置为反序列化对象的值。

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

【讨论】:

  • 而且,如果不了解类的属性,您将如何进行序列化? (以便可以将其移至超类)。
  • 您的示例显示了 BinaryFormatter(我假设来自 System.Runtime.Serialization.Formatters.Binary)。 BinaryFormatter 能够反序列化任何结构和任何标记为 [Serializable] 的类。为此,您调用 binaryFormatter.Deserialize(myStream)。我将更新我的答案以显示基类应该/不/这样做。
  • 在您的示例中,方法是静态的并返回/接收一些实体。我也可以在我的 BaseEntity 类中做到这一点,当我想这样做时问题就开始了:entity.Backup()entity.Restore()
  • 我编辑了我的帖子以提供更符合您需求的不同解决方案。
【解决方案3】:
public IEntidadBase Restore()
{
    return (IEntidadBase)bf.Deserialize(ms);
}

@jacklondon 你会如何处理 EntitySerializer 方法?

您可以使用http://www.servicestack.net/ StackService.Text 模块对干净的实体进行序列化处理。您不需要任何 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");
        }
    }

【讨论】:

  • 它不需要是静态的才能有用吗?
  • @jacklondon:是的,这可能是一个解决方案,但我希望(如果可能)将序列化包含在实体中。这样,我必须在实体类之外做:myEntity = myEntity.Restore();,这不是很好。
  • 如果每个实体都将自己序列化,该方法不能是静态的,我错了吗?
  • 我认为创建一个 EntitySerializer 会是一个更好的解决方案。你给你的实体太多的责任。 srp 是非法的。
  • 实体序列化器?有 msdn 链接吗?
【解决方案4】:

我不一定推荐这样做,但这里有一种模式可以让对象使用创建新实例的序列化来保持和恢复其自身状态:

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.)
    }
}

请注意,这仅在您的序列化程序支持非公共类时才有效。最坏的情况是,您必须将嵌套类公开,这很丑陋,但不会损害封装(因为实例是私有的)。

【讨论】:

  • 如果我错了,请纠正我,但我认为您的模型建议我将属性逐个保存到数据中。我想要一个超类,它为它的所有“子类”提供用于序列化的方法,所以这个超类不需要知道它正在序列化的实体。
  • @Diego,没错,这严格来说是允许反序列化到特定现有实例的一种方式,但使用创建新实例的序列化系统。我实际上并不建议这样做(我认为序列化由另一个类负责更有意义),但这是一种完成您所追求的部分工作的模式。
猜你喜欢
  • 2016-04-11
  • 2018-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 2023-03-30
  • 2015-11-07
  • 2019-04-10
相关资源
最近更新 更多