2014-10-30 6 views
2

Моя команда заинтересована в переносе существующей базы данных MySQL (что бывает крайне уродливо и плохо проиндексировано) на OrientDB. Поскольку наше приложение написано на C#, я изучаю нашу возможность доступа к базе данных из приложения C#.OrientDB-NET.binary для моделей

Я нашел OrientDB-NET.binary как библиотеку, чтобы делать то, что хочу, однако у меня возникают проблемы с тем, чтобы сделать все, что я хочу. В базе данных я тестирую с очень просто, из документации OrientDB:

create class Person extends V 
create class Car extends V 
create class Country extends V 

create class Owns extends E 
create class Lives extends E 

create property Owns.out LINK Person 
create property Owns.in LINK Car 
create property Lives.out LINK Person 
create property Lives.in LINK Country 

alter property Owns.out MANDATORY=true 
alter property Owns.in MANDATORY=true 
alter property Lives.out MANDATORY=true 
alter property Lives.in MANDATORY=true 

create index UniqueOwns on Owns(in) unique 
create index UniqueLives on Lives(out) unique 

Когда я запроса DB для ODocuments, я получаю много ожидаемой информации:

OClient.CreateDatabasePool("127.0.0.1", 2424, "cars", 
    ODatabaseType.Graph, "admin", "admin", 10, "Cars"); 
using (ODatabase db = new ODatabase("Cars")) 
{ 
    var results = db.Select().Form("Country").ToList(); 
    foreach (var item in results) 
    { 
     foreach(var key in item.Keys) 
     { 
      Console.WriteLine(key); 
      if (item[key] is IEnumerable<ORID>) 
      { 
       foreach (var element in item[key] as IEnumerable<ORID>) 
       { 
        Console.WriteLine(" " + element); 
       } 
      } 
      else 
      { 
       Console.WriteLine(" " + item[key]); 
      } 
     } 
     Console.WriteLine(); 
    } 
} 
Console.ReadKey(); 

Выход:

@ORID 
    #15:0 
@OVersion 
    5 
@OType 
    Document 
@OClassId 
    0 
@OClassName 
    Country 
Name 
    UK 
in_Lives 
    #16:0 

@ORID 
    #15:1 
@OVersion 
    3 
@OType 
    Document 
@OClassId 
    0 
@OClassName 
    Country 
Name 
    US 
in_Lives 
    #16:1 
    #16:2 

Если я создаю класс для представления своих вершин, я могу запросить для них:

public class Person 
{ 
    public string Name { get; set; } 
} 

public class Country 
{ 
    public string Name { get; set; } 
} 

public class Car 
{ 
    public string Name { get; set; } 
} 

//... 

var results = db.Select().From("Country").ToList<Country>(); 
foreach (var item in results) 
{ 
    Console.WriteLine(item.Name); 
} 

Выход:

UK 
US 

Однако, я хочу, чтобы иметь возможность доступа к соединениям между моими вершинами:

public class Person 
{ 
    public string Name { get; set; } 
    public Country Lives { get; set; } 
    public IEnumerable<Car> Owns { get; set; } 
} 

public class Country 
{ 
    public string Name { get; set; } 
    public IEnumerable<Person> Lives { get; set; } 
} 

public class Car 
{ 
    public string Name { get; set; } 
    public Person Owns { get; set; } 
} 

Эти параметры не будут заполняться по запросу. Тем не менее, я могу добавить списки ORID для ребер:

public class Person 
{ 
    public string Name { get; set; } 
    public List<ORID> out_Lives { get; set; } 
    public List<ORID> out_Owns { get; set; } 
} 

public class Country 
{ 
    public string Name { get; set; } 
    public List<ORID> in_Lives { get; set; } 
} 

public class Car 
{ 
    public string Name { get; set; } 
    public List<ORID> in_Owns { get; set; } 
} 

Это не работает, если я просто пытаюсь запросить вершину:

db.Select().From("Person").ToList<Person>(); 

Различные части библиотеки, по-видимому, требующий различных типов на списки ребер. (Если я правильно помню, одна часть требует от них List s, одна часть требует от них HashSet s, а одна часть требует, чтобы у них был конструктор без параметров.) Но он работает, если я запрошу конкретные края:

db.Select("Name").As("Name") 
    .Also("out('Owns')").As("out_Owns") 
    .Also("out('Lives')").As("out_Lives") 
    .From("Person").ToList<Person>(); 

К сожалению, класс ORID не содержит никакой информации, необходимой мне; это просто ссылка, которую я могу использовать в других запросах.

Мой последний оплот решение было попробовать создать регулировочные свойства, которые будут запрашивать базу данных на основе RID, просто чтобы увидеть, если я мог бы получить вещи работать:

public class Person 
{ 
    public string Name { get; set; } 
    public List<ORID> out_Owns { get; set; } 
    public List<ORID> out_Lives { get; set; } 

    public IEnumerable<Car> Owns 
    { 
     get 
     { 
      if (owns != null && 
        out_Owns != null && owns.Count() == out_Owns.Count) return owns; 
      owns = new List<Car>(); 
      if (out_Owns == null || out_Owns.Count == 0) return owns; 

      using (ODatabase db = new ODatabase("Cars")) 
      { 
       owns = db.Select("Name").As("Name") 
          .Also("in('Owns')").As("in_Owns") 
         .From(out_Owns.First()).ToList<Car>(); 
      } 
      return owns; 
     } 
    } 

    public Country Lives 
    { 
     get 
     { 
      if (lives != null) return lives; 
      if (out_Lives == null || out_Lives.Count == 0) return null; 

      using (ODatabase db = new ODatabase("Cars")) 
      { 
       lives = db.Select("Name").As("Name") 
          .Also("in('Lives')").As("in_Lives") 
          .From(out_Lives.First()).ToList<Country>() 
          .FirstOrDefault(); 
      } 
     } 
    } 

    private IEnumerable<Car> owns; 
    private Country lives; 
} 

public class Country 
{ 
    public string Name { get; set; } 
    public List<ORID> in_Lives { get; set; } 

    public IEnumerable<Person> Lives 
    { 
     get 
     { 
      if (lives != null && 
        in_Lives != null && lives.Count() == in_Lives.Count) return lives; 
      lives = new List<Person>(); 
      if (in_Lives == null || in_Lives.Count == 0) return lives; 

      using (ODatabase db = new ODatabase("Cars")) 
      { 
       StringBuilder sb = new StringBuilder(); 
       foreach (ORID id in in_Lives) 
       { 
        sb.AppendFormat("{0},", id); 
       } 
       if (sb.Length > 0) 
       { 
        sb.Remove(sb.Length - 1, 1); 
       } 
       lives = db.Select("Name").As("Name") 
          .Also("out('Owns')").As("out_Owns") 
          .Also("out('Lives')").As("out_Lives") 
          .From(string.Format("[{0}]", sb)).ToList<Person>(); 
      } 
      return lives; 
     } 
    } 

    private IEnumerable<Person> lives; 
} 

public class Car 
{ 
    public string Name { get; set; } 
    public List<ORID> in_Owns { get; set; } 

    public Person Owns 
    { 
     get 
     { 
      if (owns != null) return owns; 
      if (in_Owns == null || in_Owns.Count == 0) return null; 

      using (ODatabase db = new ODatabase("Cars")) 
      { 
       owns = db.Select("Name").As("Name") 
          .Also("out('Owns')").As("out_Owns") 
          .Also("out('Lives')").As("out_Lives") 
         .From(in_Owns.First()).ToList<Person>() 
         .FirstOrDefault(); 
      } 
     } 
    } 

    private Person owns; 
} 

Эта функция, но она выглядит совершенно ужасный код. Я запрашиваю базу данных в моих аксессуарах get (хотя кеширование результата несколько смягчает эту проблему) и делает это таким образом, что требует знания свойств связанных вершин, в средствах, которые невозможно поймать во время компиляции. Эти проблемы будут возрастать только по мере увеличения масштаба базы данных, как по количеству записей (одна из таблиц MySQL, в которую мы сейчас будем переносить более 7 миллионов строк), так и количество свойств (хотя, вероятно, это будет разбито на несколько классов , одна из таблиц MySQL имеет более 100 столбцов).

Я бы как, чтобы иметь возможность просто позвонить db.Select().From("MyVertex").ToList<MyVertex>(); и получить список MyVertex объектов со свойствами для ребер, выходящих из вершины в графе. Возможно ли это с помощью этой библиотеки? Возможно ли это с любой C# library?

+0

Официальный драйвер OrientDB .NET является: HTTPS: // GitHub. com/orientechnologies/OrientDB-NET.binary – Lvca

+0

Я нашел это вчера. Это вилка репо, с которой я связан, и хотя это несколько коммитов впереди оригинала, для моего случая использования он выполняет одинаковые действия. –

ответ

2

Так что, как говорит @Roman, база данных граф точно не разработан, чтобы достичь того, чего я ищу. Тем не менее, я разработал некоторые методы расширения, которые дают результат в любом случае, используя traverse.

В качестве предпосылки все модели, которые будут использоваться с этим решением, должны быть расширены: ABaseModel, находиться в том же пространстве имен, что и ABaseModel, и иметь конструктор без параметров. Базовый класс:

using Orient.Client; 

namespace MyApplication 
{ 
    public abstract class ABaseModel 
    { 
     public ORID ORID { get; set; } 
     public int OVersion { get; set; } 
     public ORecordType OType { get; set; } 
     public short OClassId { get; set; } 
     public string OClassName { get; set; } 
    } 
} 

Это просто обеспечивает общую базу для методов расширения, а также включая все зарезервированные свойства модели при сопоставлении с TypeMapper.

Это сделано, методы Traverse<T>(this ODatabase, string) и Traverse<T>(this ODatabase, ORID) расширить ODatabase, чтобы обеспечить требуемую функциональность:

using Orient.Client; 
using Orient.Client.Mapping; 
using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Text.RegularExpressions; 

namespace MyApplication 
{ 
    /// <summary> 
    /// Provides extension methods for <see cref="Orient.Client.ODatabase"/> 
    /// </summary> 
    public static class DatabaseExtensions 
    { 
     private const int SINGLE_RID_TARGET_PATTERN_INDEX = 3; 
     private static readonly string[] legalTargets = { 
      @"^(?:class:)?[a-zA-Z][a-zA-Z0-9]*$",  // Class 
      @"^cluster:\d+$",       // Root cluster 
      @"^\[(?:#\d+:\d+\s*,?\s*)*(?:#\d+:\d+)\]$", // Array of RIDs 
      @"^#\d+:\d+$"        // Single root record RID 
     }; 

     /// <summary> 
     /// Fills out a collection of models of type <typeparamref name="T"/> using <c>traverse</c>. <paramref name="db"/> must be open. 
     /// </summary> 
     /// <remarks> 
     /// <para>Note that <c>traverse</c> can be slow, and <c>select</c> may be more appropriate. See 
     /// http://www.orientechnologies.com/docs/last/orientdb.wiki/SQL-Traverse.html#should-i-use-traverse-or-select 
     /// </para> 
     /// <para>Lightweight edges are not followed when populating model properties. Make sure to use "heavyweight" edges with either 
     /// <c>alter property MyEdgeClass.out MANDATORY=true</c> and <c>alter property MyEdgeClass.in MANDATORY=true</c>, or else 
     /// use <c>alter database custom useLightweightEdges=false</c>.</para> 
     /// </remarks> 
     /// <typeparam name="T">The model type. Must extend <see cref="ABaseModel"/>, have a parameterless constructor, and most importantly it must be in the same 
     /// namespace as <see cref="ABaseModel"/>.</typeparam> 
     /// <param name="db">The database to query</param> 
     /// <param name="from">A class, cluster, RID list, or RID to traverse. RIDs are in the form <c>#clusterId:clusterPosition</c>. Lists are in the form 
     /// <c>[RID,RID,...]</c> with one or more elements (whitespace is ignored). Clusters are in the form <c>cluster:clusterName</c> or <c>cluster:clusterId</c>.</param> 
     /// <exception cref="System.ArgumentException">If <paramref name="from"/> is an invalid format</exception> 
     /// <returns>An enumerable collection of models of type <typeparamref name="T"/>. Public instance properties of the models will have their values populated 
     /// based on all non-lightweight edges in the traversal.</returns> 
     public static IEnumerable<T> Traverse<T>(this ODatabase db, string from) where T : ABaseModel, new() 
     { 
      // Sanity check on target 
      bool matches = false; 
      foreach (string pattern in legalTargets) 
      { 
       if (Regex.IsMatch(from, pattern)) 
       { 
        matches = true; 
        break; 
       } 
      } 

      if (!matches) 
      { 
       throw new ArgumentException("Traverse target must be a class, cluster, RID list, or single RID.", "from"); 
      } 

      bool fromSingleRecord = Regex.IsMatch(from, legalTargets[SINGLE_RID_TARGET_PATTERN_INDEX]); 

      // Traverse DB 
      string sql = string.Format("traverse * from {0}", from); 
      List<ODocument> result = db.Query(sql); 
      DatabaseTraversal traversal = new DatabaseTraversal(db, result); 

      // Process result 
      IEnumerable<T> models = traversal.ToModel<T>(); 
      if (fromSingleRecord) 
      { 
       // Either Traverse(ORID) was called, or client code called Traverse with an RID string -- return a single element 
       models = models.Where(m => m.ORID.ToString().Equals(from)); 
      } 
      return models; 
     } 

     /// <summary> 
     /// Fills out a model of type <typeparamref name="T"/> using <c>traverse</c>. <paramref name="db"/> must be open. 
     /// </summary> 
     /// <remarks> 
     /// <para>Note that <c>traverse</c> can be slow, and <c>select</c> may be more appropriate. See 
     /// http://www.orientechnologies.com/docs/last/orientdb.wiki/SQL-Traverse.html#should-i-use-traverse-or-select 
     /// </para> 
     /// <para>Lightweight edges are not followed when populating model properties. Make sure to use "heavyweight" edges with either 
     /// <c>alter property MyEdgeClass.out MANDATORY=true</c> and <c>alter property MyEdgeClass.in MANDATORY=true</c>, or else 
     /// use <c>alter database custom useLightweightEdges=false</c>.</para> 
     /// </remarks> 
     /// <typeparam name="T">The model type. Must extend <see cref="ABaseModel"/>, have a parameterless constructor, and most importantly it must be in the same 
     /// namespace as <see cref="ABaseModel"/>.</typeparam> 
     /// <param name="db">The database to query</param> 
     /// <param name="from">The root RID to traverse.</param> 
     /// <returns>A model representing the record indicated by <paramref name="from"/>.</returns> 
     public static T Traverse<T>(this ODatabase db, ORID from) where T : ABaseModel, new() 
     { 
      // Traverse<T>(from.ToString()) is guaranteed to have 0 or 1 elements 
      return db.Traverse<T>(from.ToString()).SingleOrDefault(); 
     } 

     /// <summary> 
     /// Helper class for traversing <see cref="Orient.Client.ODatabase"/> 
     /// </summary> 
     private class DatabaseTraversal 
     { 
      private IEnumerable<ODocument> documents; 
      private IEnumerable<OEdge> edges; 
      private IDictionary<ORID, ODocument> documentMap; 

      private static readonly Func<Type, bool> isModelPropertyEnumerableHelper = pType => typeof(System.Collections.IEnumerable).IsAssignableFrom(pType); 
      private static readonly Func<PropertyInfo, string> isModelPropertyHelper = pInfo => 
      { 
       string alias = pInfo.Name; 
       OProperty propertyAlias = pInfo.GetCustomAttributes(typeof(OProperty)).Where(attr => !string.IsNullOrEmpty(((OProperty)attr).Alias)).SingleOrDefault() as OProperty; 
       if (propertyAlias != null) 
       { 
        alias = propertyAlias.Alias; 
       } 

       return alias; 
      }; 
      private static readonly Action<dynamic, dynamic, string> setPropertiesHelper = (parent, child, className) => 
      { 
       PropertyInfo[] properties = parent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.GetProperty); 
       PropertyInfo propertySingle = properties.Where(prop => IsModelProperty(prop, className)).SingleOrDefault(); 
       PropertyInfo propertyCollection = properties.Where(prop => IsModelCollectionProperty(prop, className)).SingleOrDefault(); 
       if (propertySingle != null) 
       { 
        propertySingle.SetValue(parent, child); 
       } 
       else if (propertyCollection != null) 
       { 
        dynamic propertyValue = propertyCollection.GetValue(parent); 
        if (propertyValue == null) 
        { 
         Type listOfT = typeof(List<>).MakeGenericType(propertyCollection.PropertyType.GenericTypeArguments[0]); 
         IEnumerable collection = (IEnumerable)Activator.CreateInstance(listOfT); 
         propertyValue = collection; 
         propertyCollection.SetValue(parent, collection); 
        } 

        propertyValue.Add(child); 
       } 
      }; 

      /// <summary> 
      /// Create new <see cref="DatabaseTraversal"/> object. <paramref name="database"/> must be open. 
      /// </summary> 
      /// <param name="database">Database to traverse. Required for discovering edges.</param> 
      /// <param name="documents">Documents produced by <c>traverse * from $target</c></param> 
      public DatabaseTraversal(ODatabase database, IEnumerable<ODocument> documents) 
      { 
       this.documents = documents; 
       documentMap = documents.ToDictionary<ODocument, ORID>(doc => doc.ORID); 
       // Need to know which RIDs in documentMap are edges 
       edges = database.Select().From("E").ToList<OEdge>().Where(edge => documentMap.ContainsKey(edge.ORID)); 
      } 

      /// <summary> 
      /// Populate model object(s) 
      /// </summary> 
      /// <typeparam name="T">Type of model to return</typeparam> 
      /// <returns>A collection of model objects which appear in the traversal.</returns> 
      public IEnumerable<T> ToModel<T>() where T : ABaseModel, new() 
      { 
       if (documents.Count() == 0) return null; 

       IDictionary<ORID, ABaseModel> models = new Dictionary<ORID, ABaseModel>(); 
       foreach (OEdge e in edges) 
       { 
        ODocument outDoc = documentMap[e.OutV]; 
        ODocument inDoc = documentMap[e.InV]; 

        dynamic outModel, inModel; 
        bool containsOutId = models.ContainsKey(outDoc.ORID); 
        bool containsInId = models.ContainsKey(inDoc.ORID); 

        // Set the value for the models that edge is pointing into/out of 
        if (containsOutId) 
        { 
         outModel = models[outDoc.ORID]; 
        } 
        else 
        { 
         outModel = GetNewPropertyModel(typeof(T).Namespace, outDoc.OClassName); 
         MapProperties(outDoc, outModel); 
         models.Add(outModel.ORID, outModel); 
        } 

        if (containsInId) 
        { 
         inModel = models[inDoc.ORID]; 
        } 
        else 
        { 
         inModel = GetNewPropertyModel(typeof(T).Namespace, inDoc.OClassName); 
         MapProperties(inDoc, inModel); 
         models.Add(inDoc.ORID, inModel); 
        } 

        // Set the property values for outModel to inModel if they exist 
        setPropertiesHelper(outModel, inModel, e.OClassName); 
        setPropertiesHelper(inModel, outModel, e.OClassName); 
       } 

       // Return models of type T 
       IEnumerable<T> result = models.Select(kvp => kvp.Value).Where(model => model.OClassName.Equals(typeof(T).Name)).Cast<T>(); 
       return result; 
      } 

      /// <summary> 
      /// Map non-edge properties of the vertex to the model 
      /// </summary> 
      /// <typeparam name="T">The model type</typeparam> 
      /// <param name="document">The vertex</param> 
      /// <param name="resultObj">The model object</param> 
      private static void MapProperties<T>(ODocument document, T resultObj) 
      { 
       (TypeMapperBase.GetInstanceFor(typeof(T)) as TypeMapper<T>).ToObject(document, resultObj); 
      } 

      /// <summary> 
      /// Create a new instance of a model type 
      /// </summary> 
      /// <param name="nSpace">The model's namespace</param> 
      /// <param name="modelName">The model's class name</param> 
      /// <returns>A newly-initialized instance of the class <c>nSpace.modelName</c></returns> 
      private static dynamic GetNewPropertyModel(string nSpace, string modelName) 
      { 
       Type modelClass = Type.GetType(string.Format("{0}.{1}", nSpace, modelName)); 
       return modelClass.GetConstructor(Type.EmptyTypes).Invoke(null); 
      } 

      /// <summary> 
      /// Checks whether the given property or its alias is a vertex's class name and is not enumerable 
      /// </summary> 
      /// <param name="currentProperty">The property to compare name/alias against. Aliases should be set with <see cref="Orient.Client.OProperty"/></param> 
      /// <param name="name">The vertex class name to compare against</param> 
      /// <returns><see langword="true"/> if <paramref name="currentProperty"/> is named <paramref name="namne"/> or has an <see cref="Orient.Client.OProperty"/> 
      /// attribute with an alias of <paramref name="name"/>, and <paramref name="currentProperty"/> is not a collection type.</returns> 
      private static bool IsModelProperty(PropertyInfo currentProperty, string name) 
      { 
       string alias = isModelPropertyHelper(currentProperty); 
       return !isModelPropertyEnumerableHelper(currentProperty.PropertyType) && alias.Equals(name); 
      } 

      /// <summary> 
      /// Checks whether the given property or its alias is a vertex's class name and is enumerable 
      /// </summary> 
      /// <param name="currentProperty">The property to compare name/alias against. Aliases should be set with <see cref="Orient.Client.OProperty"/></param> 
      /// <param name="name">The vertex class name to compare against</param> 
      /// <returns><see langword="true"/> if <paramref name="currentProperty"/> is named <paramref name="namne"/> or has an <see cref="Orient.Client.OProperty"/> 
      /// attribute with an alias of <paramref name="name"/>, and <paramref name="currentProperty"/> is a collection type.</returns> 
      private static bool IsModelCollectionProperty(PropertyInfo currentProperty, string name) 
      { 
       string alias = isModelPropertyHelper(currentProperty); 
       return isModelPropertyEnumerableHelper(currentProperty.PropertyType) && alias.Equals(name); 
      } 
     } 
    } 
} 

Пример использования:

Person luca = db.Traverse<Person>(new ORID("#12:0")); 
// luca.Name == "Luca" 
// luca.Owns != null 
// luca.Owns.Count == 1 
// luca.Owns[0].Name == "Ferrari Modena" 
// luca.Owns[0].Owner == luca 
// luca.Lives != null 
// luca.Lives.Name == "UK" 
// luca.Lives.Residents != null 
// luca.Lives.Residents.Count == 2 
// luca.Lives.Residents[0] == luca 
// luca.Lives.Residents[1].Lives.Residents[0] == luca 
// luca.Lives.Residents[1].Owns == null -> because there is no edge to any Car 
2

Выдвинутый патч OrientDB-NET.binary

Не могли бы вы попробовать с последней версией?

Посмотрите на этот пример

using System; 
using System.Linq; 
using System.Collections.Generic; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Orient.Client; 

namespace Orient.Tests.Issues 
{ 
    // http://stackoverflow.com/questions/26661636/orientdb-net-binary-for-models 

    [TestClass] 
    public class StackOverflow_q_26661636 
    { 
     TestDatabaseContext _context; 
     ODatabase _database; 

     [TestInitialize] 
     public void Init() 
     { 
      _context = new TestDatabaseContext(); 
      _database = new ODatabase(TestConnection.GlobalTestDatabaseAlias); 

      _database.Create.Class<Person>().Extends<OVertex>().CreateProperties().Run(); 
      _database.Create.Class<Country>().Extends<OVertex>().CreateProperties().Run(); 
      _database.Create.Class<Car>().Extends<OVertex>().CreateProperties().Run(); 
      _database.Create.Class("Owns").Extends<OEdge>().Run(); 
      _database.Create.Class("Lives").Extends<OEdge>().Run(); 

     } 

     [TestCleanup] 
     public void Cleanup() 
     { 
      _database.Dispose(); 
      _context.Dispose(); 
     } 

     [TestMethod] 
     [TestCategory("Stackoverflow")] 
     public void q_26661636() 
     { 
      var lukaPerson = new Person { Name = "Luca" }; 
      var lpV = _database.Create.Vertex(lukaPerson).Run(); 

      var ferrariModenaCar = new Car { Name = "Ferrari Modena" }; 
      var fmcV = _database.Create.Vertex(ferrariModenaCar).Run(); 
      var bmwCar = new Car { Name = "BMW" }; 
      var bmwcV = _database.Create.Vertex(bmwCar).Run(); 
      var lp_fmcE = _database.Create.Edge("Owns").From(lpV.ORID).To(fmcV.ORID).Run(); 
      var lp_bmwcE = _database.Create.Edge("Owns").From(lpV.ORID).To(bmwcV.ORID).Run(); 

      var countryUS = new Country { Name = "US" }; 
      var uscV = _database.Create.Vertex(countryUS).Run(); 
      var lp_uscE = _database.Create.Edge("Lives").From(lpV.ORID).To(uscV.ORID).Run(); 

      var countryUK = new Country { Name = "UK" }; 
      var ukcV = _database.Create.Vertex(countryUK).Run(); 

      var pl = _database.Select().From<Person>().ToList<Person>().FirstOrDefault(p => p.Name == lukaPerson.Name); 

      Assert.IsNotNull(pl); 
      Assert.AreEqual(lukaPerson.Name, pl.Name); 
      Assert.AreEqual(1, pl.out_Lives.Count); 
      Assert.AreEqual(2, pl.out_Owns.Count); 
     } 
    } 

    public class Person 
    { 
     public string Name { get; set; } 
     public List<ORID> out_Lives { get; set; } 
     public List<ORID> out_Owns { get; set; } 
    } 

    public class Country 
    { 
     public string Name { get; set; } 
     public List<ORID> in_Lives { get; set; } 
    } 

    public class Car 
    { 
     public string Name { get; set; } 
     public List<ORID> in_Owns { get; set; } 
    } 
} 
+0

Это, похоже, решает проблему необходимости знать детали целевой модели, чтобы получить список ORID, но есть ли способ заполнить модель типом данных вершины, на которую указывают ORID? –

+1

вы работаете с базой данных графа, если хотите, чтобы все данные в одном документе вам нужны для использования базы данных документов со встроенным типом поля. В этой ситуации вам нужно использовать '_database.Load.ORID () .Run()' –

+0

Проверено снова и если вы запросите Person с таким образом: var pl = _database.Select(). От (). ToList («*: - 1») . FirstOrDefault (p => p.ORID == lpV.ORID) . To (); 'записи предварительно заполнены и в _database.ClientCache у вас есть все ваши записи –

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