2009-12-21 24 views
8

Tôi có một lớp, chứa một từ điển tĩnh của tất cả các cá thể hiện có, được định nghĩa tại thời gian biên dịch.C# DataContract Serialization, làm thế nào để deserialize cho trường hợp đã tồn tại

Về cơ bản nó trông như thế này:

[DataContract] 
class Foo 
{ 
    private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>(); 

    [DataMember] 
    private long id; 

    public static readonly Foo A = Create(1); 
    public static readonly Foo B = Create(2); 
    public static readonly Foo C = Create(3); 

    private static Foo Create(long id) 
    { 
    Foo instance = new Foo(); 
    instance.id = id; 
    instances.Add(instance); 
    return instance; 
    } 

    public static Foo Get(long id) 
    { 
    return instances[id]; 
    }  

} 

Có các lĩnh vực khác, và lớp có nguồn gốc, nhưng điều này không quan trọng cho vấn đề.

Chỉ được nối tiếp id. Khi một thể hiện kiểu này được deserialized, tôi muốn có được thể hiện đã được tạo ra như là trường tĩnh (A, B hoặc C), sử dụng Foo.Get(id) thay vì nhận một phiên bản mới.

Có cách nào đơn giản để thực hiện việc này không? Tôi không tìm thấy bất kỳ tài nguyên nào mà tôi có thể hiểu được.

Trả lời

16

Trong deserialization nó (AFAIK) luôn luôn sử dụng một đối tượng mới (FormatterServices.GetUninitializedObject), nhưng để có được nó để thay thế các đối tượng sau deserialization (nhưng trước khi họ được trả lại cho người gọi), bạn có thể thực hiện IObjectReference, như vậy:

[DataContract] 
class Foo : IObjectReference { // <===== implement an extra interface 
    object IObjectReference.GetRealObject(StreamingContext ctx) { 
     return Get(id); 
    } 
    ...snip 
} 

done ... bằng chứng:

static class Program { 
    static void Main() { 
     Foo foo = Foo.Get(2), clone; 
     DataContractSerializer ser = new DataContractSerializer(typeof(Foo)); 
     using (MemoryStream ms = new MemoryStream()) { // clone it via DCS 
      ser.WriteObject(ms, foo); 
      ms.Position = 0; 
      clone = (Foo)ser.ReadObject(ms); 
     } 
     Console.WriteLine(ReferenceEquals(foo, clone)); // true 
    } 
} 

Lưu ý có một số lưu ý thêm về vấn đề này cho các kịch bản tin tưởng một phần trên MSDN, here.

3

Tôi đã gặp phải sự cố tương tự và giải pháp tốt nhất mà tôi đã tìm thấy là thêm một số lớp trình bao bọc, đã quản lý các phiên bản của lớp cần được sắp xếp.

Tôi không chắc chắn về chữ ký chính xác với Hợp đồng. Tôi sử dụng SerializableAttribute, và với nó tôi nhìn smth. như thế:

[Serializable] 
class FooSerializableWrapper : ISerializable 
{ 
    private readonly long id; 

    public Foo Foo 
    { 
     get 
     { 
      return Foo.Get(id); 
     } 
    } 

    public FooSerializableWrapper(Foo foo) 
    { 
     id = foo.id; 
    } 

    protected FooSerializableWrapper(SerializationInfo info, StreamingContext context) 
    { 
     id = info.GetInt64("id"); 
    } 


    void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("id", id); 
    } 

} 
+0

Tôi thực sự không muốn lớp sử dụng được quan tâm, bởi vì 'Foo' là một lớp rất trung tâm và sử dụng từ nhiều loại khác. –

+0

Cách triển khai này trông như thế nào? –

+0

vui lòng xem câu trả lời đã chỉnh sửa của tôi – ironic

0

Bạn có thể có được một bước tiến tới những gì bạn đang tìm kiếm bằng cách sử dụng OnDeserializingAttribute. Tuy nhiên, điều đó có lẽ sẽ chỉ cho phép bạn đặt thuộc tính (vì vậy bạn có thể có số tiền cho một phương thức Sao chép điền tất cả các thuộc tính của cá thể hiện tại bằng cách sử dụng cá thể tĩnh của bạn bằng cách sử dụng đối tượng tĩnh của bạn. , có lẽ bạn sẽ phải viết deserializer riêng của bạn ...

chưa được kiểm tra, nhưng tôi sẽ cho rằng bạn có thể thực hiện một deserializer khá dễ dàng như thế này:

public class MyDeserializer : System.Xml.Serialization.XmlSerializer 
{ 
    protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader) 
    { 
     Foo obj = (Foo)base.Deserialize(reader); 
     return Foo.Get(obj.id); 
    } 
} 

Lưu ý rằng bạn sẽ phải làm điều gì đó về việc nhận ID vì nó là riêng tư trong mã của bạn, Ngoài ra điều này giả định bạn đang sử dụng tuần tự hóa XML; Thay thế các thừa kế với bất cứ điều gì bạn thực sự đang sử dụng. Và cuối cùng, điều này có nghĩa là bạn sẽ phải khởi tạo loại này khi deserializing các đối tượng của bạn, có thể liên quan đến việc thay đổi một số mã và/hoặc cấu hình.

+0

Tôi biết thuộc tính 'OnDeserializing'. Nó không cho phép tạo cá thể, bởi vì nó được gọi trên cá thể đã được tạo ra. Ngoài ra còn có giao diện 'ISerializable' và các công cụ khác, nhưng tôi không thể tìm ra giải pháp cho việc này. –

+0

Xem thêm thông tin về cách xây dựng Deserializer của riêng bạn. –

+0

Tôi cần nó cho WCF (NetDataContractSerializer). Tôi * có thể * sử dụng bộ nối tiếp khác cho toàn bộ ứng dụng, nhưng tôi muốn giữ cơ sở hạ tầng càng nhiều càng tốt. –

0

Không sao, chỉ cần sử dụng 2 lớp.Trong phương pháp getObject bạn nhận được đối tượng hiện tại của bạn

[Serializable] 
public class McRealObjectHelper : IObjectReference, ISerializable 
{ 
    Object m_realObject; 
    virtual object getObject(McObjectId id) 
    { 
     return id.GetObject(); 
    } 
    public McRealObjectHelper(SerializationInfo info, StreamingContext context) 
    { 
     McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId)); 
     m_realObject = getObject(id); 
     if(m_realObject == null) 
      return; 
     Type t = m_realObject.GetType(); 
     MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context); 
     List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length); 
     List<object> data = new List<object>(members.Length); 
     foreach(MemberInfo mi in members) 
     { 
      Type dataType = null; 
      if(mi.MemberType == MemberTypes.Field) 
      { 
       FieldInfo fi = mi as FieldInfo; 
       dataType = fi.FieldType; 
      } else if(mi.MemberType == MemberTypes.Property){ 
       PropertyInfo pi = mi as PropertyInfo; 
       dataType = pi.PropertyType; 
      } 
      try 
      { 
       if(dataType != null){ 
        data.Add(info.GetValue(mi.Name, dataType)); 
        deserializeMembers.Add(mi); 
       } 
      } 
      catch (SerializationException) 
      { 
       //some fiels are missing, new version, skip this fields 
      } 
     } 
     FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray()); 
    } 

    public object GetRealObject(StreamingContext context) 
    { 
     return m_realObject; 
    } 
    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] 
    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
    } 
} 

public class McRealObjectBinder: SerializationBinder 
{ 
    String assemVer; 
    String typeVer; 
    public McRealObjectBinder(String asmName, String typeName) 
    { 
     assemVer = asmName; 
     typeVer = typeName; 
    } 
    public override Type BindToType(String assemblyName, String typeName) 
    { 
     Type typeToDeserialize = null; 
     if (assemblyName.Equals(assemVer) && typeName.Equals(typeVer)) 
     { 
      return typeof(McRealObjectHelper); 
     } 
     typeToDeserialize = Type.GetType(String.Format( "{0}, {1}", typeName, assemblyName)); 
     return typeToDeserialize; 
    } 
} 

Sau đó, khi deserialize:

BinaryFormatter bf = new BinaryFormatter(null, context); 
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName); 
bf.Deserialize(memStream); 
Các vấn đề liên quan