2013-09-08 3 views
0

Скажем, у меня есть следующие (упрощенно):neo4jclient гетерогенные данные вернуться

public class Item 
{ 
    public String Name { get; set; } 
    public String Type { get; set; } 
} 

public class Armor : Item 
{ 
    public int AC { get; set; } 
    public Armor() { Type = "Armor"; } 
} 

public class Weapon : Item 
{ 
    public int Damage { get; set; } 
    public Armor() { Type = "Weapon"; } 
} 

public class Actor 
{ 
    ... 
} 

public class HasItem : Relationship<ItemProps>, IRelationshipAllowingSourceNode<Actor>, IRelationshipAllowingTargetNode<Item> 
{ 
    public readonly string TypeKey = "HasItem"; 

    public HasItem (NodeReference targetItem, int count = 1) 
     : base(targetItem, new ItemProps { Count = count }) 
    { 
    } 

    public override string RelationshipTypeKey 
    { 
     get { return TypeKey; } 
    } 
} 

С помощью этой установки можно легко создать гетерогенную список оружия, брони и т.д., связанные с Актера. Но я не могу понять, как их вытащить. У меня есть этот метод (снова упрощенный), чтобы получить список всех связанных элементов, но он получает их все как элементы. Я не могу понять, как получить их как их фактический тип. Я могу использовать поле Type, чтобы определить тип, но не похоже, чтобы быть в любом случае динамически строит обратное:

public IEnumerable<Item> Items 
    { 
     get 
     { 
      return 
      GameNode 
       .GraphClient 
       .Cypher 
       .Start(new { a = Node.ByIndexLookup("node_auto_index", "Name", Name) }) 
       .Match("(a)-[r:HasItem]-(i)") 
       .Return<Item>("i") // Need something here to return Armor, Weapon, etc as needed based on the Type property 
       .Results; 
     } 
    } 

я нашел плохой обходной путь, где я вернусь тип и NodeID и запустить список с помощью оператор switch, который выполняет .Get с NodeID и передает его правильному типу. но это негибко и неэффективно. Я мог бы запускать один запрос для каждого производного класса и объединять их вместе, но мысль об этом заставляет меня сканировать кожу.

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

ответ

3

Проблема в том, как данные хранятся в Neo4J и сериализованы через Json.net.

Скажем, у меня есть меч:

var sword = new Weapon{ 
    Name = "Sword 12.32.rc1", 
    Type = "Sword" 
    Damage = 12 
}; 

Если сериализовать это Neo4j: graphClient.Create(sword); все прекрасно, внутренне мы теперь имеем представление JSON, который будет выглядеть примерно так:

{ "Name" : "Sword 12.32.rc1", "Type": "Sword", "Damage": "12"} 

Здесь нет информации о том, что компьютер может использовать для вывода, что это на самом деле тип «Меч», поэтому, если вы вернете коллекцию типа Item, она может вернуть только два свойства: Name и Type.

Итак, есть два решения, о которых я могу думать, ни один из них не является замечательным, но оба из них помогут вам с одним запросом. Первый (наиболее Sucky) заключается в создании «SuperItem», который имеет все свойства из производных классов вместе, так:

public class SuperItem { Name, Type, Damage, AC } //ETC 

Но это ужасно, и делает вид, имеющий иерархию бессмысленно. Второй вариант, который пока не велик лучше - это использовать Dictionary, чтобы получить данные:

var query = GraphClient 
    .Cypher 
    .Start(new {n = actorRef}) 
    .Match("n-[:HasItem]->item") 
    .Return(
    item => new 
    { 
     Item = item.CollectAs<Dictionary<string,string>>() 
    }); 

var results = query.Results.ToList(); 

Что, если вы бежите:

foreach (var data in results2.SelectMany(item => item.Item, (item, node) => new {item, node}).SelectMany(@t => @t.node.Data)) 
    Console.WriteLine("Key: {0}, Value: {1}", data.Key, data.Value); 

печати бы из:

Key: Type, Value: Sword 
Key: Damage, Value: 12 
Key: Name, Value: 12.32.rc1 

Итак, теперь у нас есть словарь свойств, мы можем создать класс расширения для его анализа:

public static class DictionaryExtensions 
{ 
    public static Item GetItem(this Dictionary<string, string> dictionary) 
    { 
     var type = dictionary.GetTypeOfItem().ToLowerInvariant(); 
     var json = dictionary.ToJson(); 
     switch (type) 
     { 
      case "sword": 
       return GetItem<Weapon>(json); 

      case "armor": 
       return GetItem<Armor>(json); 

      default: 
       throw new ArgumentOutOfRangeException("dictionary", type, string.Format("Unknown type: {0}", type)); 
     } 
    } 

    private static string GetTypeOfItem(this Dictionary<string, string> dictionary) 
    { 
     if(!dictionary.ContainsKey("Type")) 
      throw new ArgumentException("Not valid type!"); 

     return dictionary["Type"]; 
    } 

    private static string ToJson(this Dictionary<string, string> dictionary) 
    { 
     var output = new StringBuilder("{"); 

     foreach (var property in dictionary.OrderBy(k => k.Key)) 
      output.AppendFormat("\"{0}\":\"{1}\",", property.Key, property.Value); 

     output.Append("}"); 
     return output.ToString(); 
    } 

    private static Item GetItem<TItem>(string json) where TItem: Item 
    { 
     return JsonConvert.DeserializeObject<TItem>(json); 
    } 
} 

и использовать что-то вроде:

var items = new List<Item>(); 
foreach (var data in results) 
    foreach (Node<Dictionary<string, string>> item in data.Item) 
     items.Add(item.Data.GetItem()); 

Где items будут типы вы после этого.

I знаю Это не очень удобно, но это дает вам один запрос.

+0

Это именно то, что я искал. Я знал, как работает сериализация, и почему у меня возникла проблема. Я просто не знал, как вернуть «сырые» данные, чтобы я мог свернуть свои собственные объекты. Большое вам спасибо за вашу помощь! –