2010-06-20 2 views
8

var store = GetStore(); с использованием (IsolatedStorageFileStream fileStream = store.OpenFile (RootData, FileMode.Create)) { DataContractSerializer serializer = новый DataContractSerializer (typeof (List)); serializer.WriteObject (fileStream, rootdatalist); }сериализовать объект с подпунктами C#

Но это только сериализует rootdatalist, а не подэлементы. У rootdatalist есть свойство List List, как это сделать сериализуем, чтобы получить иерархию списка?

Поскольку это DBML сгенерированных объекты Узлов свойство корня

public System.Data.Linq.Table<Node> Nodes 
{ 
    get 
    { 
     return this.GetTable<Node>(); 
    } 
} 

Моего DataContext возвращение:

public List<Root> GetRootList(Guid userid) 
{ 
    DataLoadOptions loadopts = new DataLoadOptions(); 
    loadopts.LoadWith<Root>(s => s.Nodes); 
    this.DataContext.LoadOptions = loadopts; 
    return this.DataContext.Root.Where(s => s.Nodes.Count(n => n.UserId == userid) > 0).ToList(); 
} 

узел EntitySet выглядит следующим образом в моем DBML дизайнере

[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Root_Node", Storage="_Nodes", ThisKey="Id", OtherKey="RootId")] 
[global::System.Runtime.Serialization.DataMemberAttribute(Order=5, EmitDefaultValue=false)] 
public EntitySet<Node> Nodes 
{ 
    get 
    { 
     if ((this.serializing && (this._Nodes.HasLoadedOrAssignedValues == false))) 
     { 
      return null; 
     } 
     return this._Nodes; 
    } 
    set 
    { 
     this._Nodes.Assign(value); 
    } 
} 

Также у меня должен быть тег [Include] выше моих свойств или nothi ng будет загружен. Edit :: Для других желающих сериализации DBML классы http://blogs.msdn.com/b/wriju/archive/2007/11/27/linq-to-sql-enabling-dbml-file-for-wcf.aspx

+0

Есть ли данные [DataMember] над 'Nodes'? А что такое «Корень» здесь? Тот факт, что он предоставляет «Таблица », заставляет меня думать, что это «DataContext», но «DataContext» не является (AFAIK), который должен быть частью сериализуемой модели - это скорее менеджер, чем объект домена. (регулярная связь от однонаправленного типа, созданного dbml, будет 'EntitySet ', а не 'Таблица ') –

+0

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

+0

@Jakob - точно; * data-context * не предназначен для сериализации. Только ваши объекты * домена * становятся сериализуемыми. Однако; Какова ваша цель здесь? У меня может быть больше нескольких трюков в рукаве ... (я смиренно заявляю, что я * болезненно * знаком с сериализацией .NET в нескольких обличьях) –

ответ

9

Вы можете включать в себя больше информации о типах контракта? Я бы ожидать, что, например, Root помечается как дата-контракт с членом как член-данных, например:

[DataContract] 
public class Root { 
    [DataMember] 
    public List<SubItem> Nodes {get;private set;} 
} 

[DataContract] 
public class SubItem { 
    [DataMember] 
    public int Foo {get;set;} 

    [DataMember] 
    public string Bar {get;set;} 
} 

Это должно работать. Если нет, это действительно поможет увидеть ваши типы (или их сокращенную версию, что иллюстрирует проблему).

7

В DataContractSerializer нужно знать о все типов в вашем графике объекта.

Используйте constructor overload, что позволяет определить те, а также корневой тип:

DataContractSerializer serializer = new DataContractSerializer(typeof(List<Root>), listOfOtherTypes); 
+0

будет ли это работать, если Список на самом деле EntityRef в классе Root? – Jakob

+0

@Jakob - Он должен, пока вы говорите сериализатору о типе. См. Также ответ от Marc, так как вам также нужно правильно указать свои 'DataContract' и' DataMember'. – Oded

+0

Поскольку это сгенерировано, контракты должны быть уже правильными. И в * большинстве * сценариях ** не требуется предоставлять дополнительные данные для ctor. В частности, наследование может быть рассмотрено с помощью '[KnownType]' и т. Д. –

0

OK; Мне пришлось повторить это, используя таблицы Person/PersonPhone (в схеме Person) из новой установки AdventureWorks2008R2.

У меня есть контекст данных, установленный в серию «Однонаправленная», со стандартными привязками LINQ-to-SQL и т. Д. (Без настроек).

Для сравнения со своим сценарием Person может иметь PersonPhone, и нас интересует List<Person>.Я смотрел 3 сценариев, каждый смотрит на полный набор данных:

  1. сериализации ванильный набор Person записей
  2. так же, как 1, но с использованием LoadWith, чтобы получить записи телефонных
  3. то же, что и 1, но вручную итерации данных (примечание: это может вызвать проблемы с N + 1)

Вот результаты; как вы можете видеть, 1 терпит неудачу в том, как вы описываете, но 2 & 3 работают отлично, с отличием, что 3 делает значительно больше работы TSQL.

Так без дальнейших подробностей (в идеале полностью воспроизводимые например), то очень трудно, чтобы исследовать дальше ...

Результаты:

Default 
======= 
Bytes: 20219898 
Original person count: 19972 
Original phone count: 0 
Deser person count: 19972 
Deser phone count: 0 
Log: 1140 

LoadWith 
======== 
Bytes: 24767996 
Original person count: 19972 
Original phone count: 19972 
Deser person count: 19972 
Deser phone count: 19972 
Log: 2517 

Enumerated 
========== 
Bytes: 24767996 
Original person count: 19972 
Original phone count: 19972 
Deser person count: 19972 
Deser phone count: 19972 
Log: 6322697 

Испытательный стенд:

class Program 
{ 

    static void Main(string[] args) 
    { 
     using(var dc = new DataClasses1DataContext()) 
     { // 1: vanilla 
      dc.Log = new StringWriter(); 
      RoundtripAndCount("Default", dc.Persons); 
      Console.WriteLine("Log: " + dc.Log.ToString().Length); 
     } 
     using (var dc = new DataClasses1DataContext()) 
     { // 2: LoadWith 
      dc.Log = new StringWriter(); 
      var opt = new DataLoadOptions(); 
      opt.LoadWith<Person>(p => p.PersonPhones); 
      dc.LoadOptions = opt; 
      RoundtripAndCount("LoadWith", dc.Persons); 
      Console.WriteLine("Log: " + dc.Log.ToString().Length); 
     } 
     using (var dc = new DataClasses1DataContext()) 
     { // 3: iterate manually 
      dc.Log = new StringWriter(); 
      // manually iterate the data (LINQ-to-Objects) 
      GC.KeepAlive(dc.Persons.AsEnumerable().Sum(p=>p.PersonPhones.Count())); // just an opaque method 
      RoundtripAndCount("Enumerated", dc.Persons); 
      Console.WriteLine("Log: " + dc.Log.ToString().Length); 
     } 
    } 

    static void RoundtripAndCount(string caption, IEnumerable<Person> people) 
    { 
     Console.WriteLine(); 
     Console.WriteLine(caption); 
     Console.WriteLine(new string('=', caption.Length)); 
     List<Person> list = people.ToList(), clone; 
     using(var ms = new MemoryStream()) 
     { 
      var ser = new DataContractSerializer(typeof (List<Person>)); 
      ser.WriteObject(ms, list); 
      ms.Position = 0; 
      clone = (List<Person>) ser.ReadObject(ms); 
      Console.WriteLine("Bytes: " + ms.Length); 
     } 
     Func<Person, int> phoneCount = p => p.PersonPhones.HasLoadedOrAssignedValues ? p.PersonPhones.Count() : 0; 
     Console.WriteLine("Original person count: " + people.Count()); 
     Console.WriteLine("Original phone count: " + people.Sum(phoneCount)); 

     Console.WriteLine("Deser person count: " + clone.Count()); 
     Console.WriteLine("Deser phone count: " + clone.Sum(phoneCount)); 

    } 
} 

В качестве примечания, я может t слабый protobuf-net, чтобы убедить L2S в предоставлении ему данных (где DataContractSerializer решает проигнорировать его), но это воспроизводит сценарий 3 с огромной стоимостью N + 1. Следовательно, я не планирую заниматься этим ...

+0

@Marc - вы хотели бы иметь общий вид с ms teamviewer или что-то в этом роде? – Jakob

+0

@Marc - есть ли теги [include] в метаданных? Я должен иметь их в своем проекте, или ничего не загружается. – Jakob

+0

@ Якоб - я проверю. Например, образец находится на моей другой машине. –

Смежные вопросы