在RavenDb中进行一对多模型以获得更好的性能

时间:2017-01-09 11:54:07

标签: c# relationship ravendb document-database

我正在接近文档数据库,我有点困惑如何映射文档关系,情况如下

public class Person
{
    public Person()
    {
    }
    public int Id { get; set; }
    public string Name { get;set;}
    public string Surname { get; set; }
    public DateTime? BirthDate { get; set; }
}


public class Car
{
    public Car() { }
    public int Id { get; set; }
    public string Name { get; set; }
    public int PersonId { get; set;}
}

一个人有一辆或多辆汽车,例如我可以按照以下方式查询数据库

public Car Get(int id)
    {
        Car car = null;
        using (IDocumentSession session = store.OpenSession())
        {
            car = session.Include<Car, Person>(x => x.PersonId).Load<Car>(id);
            bool isLoaded = session.Advanced.IsLoaded("people/" + car.PersonId); // true!
        }
        return car;
    }

并且一切正常,客户端只发出一个请求,但如果我有一个人并且我想要显示他的所有汽车我怎样才能查询数据库来执行请求? 我想我必须修改模型,在List<int> Cars中加Person作为参考他的车。 请注意,我不想在Cars文档中嵌入Person,因为Cars可以从其他文档中引用。

感谢。

2 个答案:

答案 0 :(得分:3)

您可以索引Cars集合并从索引中加载所有汽车。

索引如下所示:

public class CarIndex : AbstractIndexCreationTask<Car, CarView>
{
    public CarIndex()
    {
        Map = cars => from car in cars
                      select new
                      {
                          car.Id,
                          car.Name,
                          car.PersonId,
                      };
    }
}

CarView类与Car类相同,但可以更改以更好地满足索引需求。

public class CarView
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int PersonId { get; set; }
}

您需要先执行索引才能使用它:

new CarIndex().Execute(store);

为某个人装载汽车看起来像这样:

using (IDocumentSession session = store.OpenSession())
{
    session.Store(new Person { Id = 1, Name = "A", Surname = "A" });
    session.Store(new Car { Id = 1, Name = "A", PersonId = 1 });
    session.Store(new Car { Id = 2, Name = "B", PersonId = 1 });
    session.Store(new Car { Id = 3, Name = "C", PersonId = 2 });
    session.SaveChanges();
}

WaitForIndexing(store); // from RavenTestBase

using (IDocumentSession session = store.OpenSession())
{
    var resultsForId1 = session
        .Query<CarView, CarIndex>()
        .ProjectFromIndexFieldsInto<CarView>()
        .Where(x => x.PersonId == 1);
    Assert.Equal(2, resultsForId1.Count());
    var resultsForId2 = session
        .Query<CarView, CarIndex>()
        .ProjectFromIndexFieldsInto<CarView>()
        .Where(x => x.PersonId == 2);
    Assert.Equal(1, resultsForId2.Count());
}

如果您想在单个数据库请求中加载此人及其汽车,请使用延迟加载:

var resultsForId1 = session
    .Query<CarView, CarIndex>()
    .ProjectFromIndexFieldsInto<CarView>()
    .Where(x => x.PersonId == 1).Lazily();
var person = session.Advanced.Lazily.Load<Person>(1);

var personValue = person.Value;
var resultsValue = resultsForId1.Value;

完成测试(需要xunit和RavenDB.Tests.Helpers nugets):

using Raven.Client;
using Raven.Client.Indexes;
using Raven.Tests.Helpers;
using System;
using System.Linq;
using Xunit;

namespace SO41547501Answer
{
    public class SO41547501 : RavenTestBase
    {
        [Fact]
        public void SO41547501Test()
        {
            using (var server = GetNewServer())
            using (var store = NewRemoteDocumentStore(ravenDbServer: server))
            {
                new CarIndex().Execute(store);

                using (IDocumentSession session = store.OpenSession())
                {
                    session.Store(new Person { Id = 1, Name = "A", Surname = "A" });
                    session.Store(new Car { Id = 1, Name = "A", PersonId = 1 });
                    session.Store(new Car { Id = 2, Name = "B", PersonId = 1 });
                    session.Store(new Car { Id = 3, Name = "C", PersonId = 2 });
                    session.SaveChanges();
                }

                WaitForAllRequestsToComplete(server);
                WaitForIndexing(store);

                using (IDocumentSession session = store.OpenSession())
                {
                    var resultsForId1 = session
                        .Query<CarView, CarIndex>()
                        .ProjectFromIndexFieldsInto<CarView>()
                        .Where(x => x.PersonId == 1);
                    Assert.Equal(2, resultsForId1.Count());
                    var resultsForId2 = session
                        .Query<CarView, CarIndex>()
                        .ProjectFromIndexFieldsInto<CarView>()
                        .Where(x => x.PersonId == 2);
                    Assert.Equal(1, resultsForId2.Count());
                }

                using (IDocumentSession session = store.OpenSession())
                {
                    server.Server.ResetNumberOfRequests();
                    var resultsForId1 = session
                        .Query<CarView, CarIndex>()
                        .ProjectFromIndexFieldsInto<CarView>()
                        .Where(x => x.PersonId == 1).Lazily();
                    var person = session.Advanced.Lazily.Load<Person>(1);

                    var personValue = person.Value;
                    var resultsValue = resultsForId1.Value;
                    Assert.Equal("A", personValue.Name); // person data loaded
                    Assert.Equal("A", resultsValue.First().Name); // cars data loaded
                    Assert.Equal(1, server.Server.NumberOfRequests); // only one request sent to the server
                }
            }
        }
    }

    public class CarIndex : AbstractIndexCreationTask<Car, CarView>
    {
        public CarIndex()
        {
            Map = cars => from car in cars
                          select new
                          {
                              car.Id,
                              car.Name,
                              car.PersonId,
                          };
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public DateTime? BirthDate { get; set; }
    }

    public class Car
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int PersonId { get; set; }
    }

    public class CarView
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int PersonId { get; set; }
    }
}

答案 1 :(得分:2)

你可以这样做:

using (IDocumentSession session = store.OpenSession())
{
    var carsForOne = session.Query<Car>()
             .Include(x=>x.PersonId)
             .Where(x=>x.PersonId == "people/1")
             .ToList();

    var person = session.Load<Person>("people/1");
}

这只需要一个db请求。