neo4jclient异构数据返回

时间:2013-09-08 18:15:44

标签: c# 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; }
    }
}

通过这种设置,我可以轻松创建与Actor相关的异类武器,护甲等列表。但我似乎无法弄清楚如何解决这些问题。我有这个方法(再次简化)来获取所有相关项目的列表,但它将它们全部作为项目获取。我无法弄清楚如何将它们作为实际类型。我可以使用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;
        }
    }

我找到了一个糟糕的解决方法,我返回Type和NodeID并通过switch语句运行列表,该语句使用NodeID执行.Get并将其强制转换为正确的类型。但这是不灵活和低效的。我可以为每个派生类运行一个查询并将它们连接在一起,但想到这一点会让我的皮肤爬行。

这似乎是一个常见的问题,但我在网上找不到任何东西。有什么想法吗?

1 个答案:

答案 0 :(得分: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"}

此处没有任何信息,计算机可以使用它来推断这实际上是'Sword'类型,所以如果你带回一个Item类型的集合,它只能带回两个属性{{ 1}}和Name

所以,有两个我能想到的解决方案,其中一个都不是很好,但两者都能让你得到一个查询解决方案。第一个(最糟糕的)是创建一个'SuperItem',它将派生类中的所有属性放在一起,所以:

Type

但这太可怕了,有点让层次结构毫无意义。第二个选项虽然不是很好但是更好 - 是使用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()); 将成为你所追求的类型。

知道这不是很好,但确实可以让你进行一次查询。

相关问题