c# – Nest无法应对EF6 / MVC5的大型数据库模型

我已经获得了一个数据库,可以根据该数据库进行基本的CRUD操作.这很快就通过使用.NET 4.5 / MVC5和EF6来完成.这意味着数据库优先方法.

新要求:(弹性)搜索.

在为自定义类创建索引(未链接到模型中的其他类)时,一切都很好.当我使用带有很多外键的类时,事情就会停止工作.该数据库包含100个表,包含400个外键.

我认为问题可能是循环引用(客户有n个合同,其中有一个对客户的引用,其中有一个合同列表,……你得到了图片).最终我得到了一个OutOfMemory异常,一切都崩溃了.

码:

public static Uri node;
public static ConnectionSettings settings;
public static ElasticClient client;

public ActionResult TestIndex()
    {
        node = new Uri("http://localhost:9200");
        settings = new ConnectionSettings(node, defaultIndex: "crudapp");
        client = new ElasticClient(settings);

        var indexSettings = new IndexSettings();
        indexSettings.NumberOfReplicas = 1;
        indexSettings.NumberOfShards = 1;

        //The next line causes the OutOfMemoryException
        client.CreateIndex(c => c.Index("crudapp")
                                 .InitializeUsing(indexSettings)
                                 .AddMapping<Customer>(map => map.MapFromAttributes(maxRecursion: 1)));


        foreach (Customer c in db.Customer.Where(a => a.Active == true))
            client.Index(c);

        return View("Index");
    }

如何告诉Nest停止递归或不使用某些对象?

样本类:

    public partial class Customer
    {
        public Customer()
        {
            this.CustomerContract = new HashSet<CustomerContract>();
        }

        public int Customerid { get; set; }
        public string CustomerName { get; set; }
        public string Description { get; set; }
        public bool Active { get; set; }

        public virtual ICollection<CustomerContract> CustomerContract { get; set; }
    }

    public partial class CustomerContract
    {
        public CustomerContract()
        {
            this.Host = new HashSet<Host>();
        }

        public int CustomerContractid { get; set; }
        public string CustomerContractName { get; set; }
        public string Description { get; set; }
        public int CustomerID { get; set; }
        public bool Active { get; set; }

        public virtual Customer Customer { get; set; }
        public virtual ICollection<Host> Host { get; set; }
    }

最佳答案 OutOfMemoryException几乎肯定来自Customer对象的JSON序列化.因此,问题不是NEST或Elasticsearch功能,而是JSON.NET功能.

您可以通过以下两种方式之一处理此问题:

1.有选择地序列化大对象

This article由JSON.NET的作者讨论减少对象的大小.您可以使用JsonIgnoreAttribute property提供属性,以指示序列化程序忽略某些属性.或者IContractResolver的实现可能对EF对象的定义不那么具有侵入性(特别是考虑到它们是数据库优先生成的),但我不确定这是否可以与NEST依赖于JSON.NET一起使用.

如果您没有选择处理NEST对JSON.NET的依赖,那么您总是可以找到另一种方法来序列化您的对象并使用Elasticsearch.NET语法而不是NEST(它基本上构建于Elasticsearch.NET).因此,不是调用ElasticClient.Index(..),而是调用ElasticClient.Raw.Index(..),其中body参数是您要索引的对象的JSON字符串表示(您自己的构造) .

2.将大对象投影到较小的数据传输对象

不是索引Customer对象,而是仅将要索引的属性映射到以Elasticsearch架构/文档类型为目标的数据传输对象(DTO).

foreach (Customer c in db.Customer.Where(a => a.Active == true))
    client.Index(new MyElasticsearchTypes.Customer()
        {
            CustomerId = c.CustomerId,
            CustomerName = c.CustomerName,
            Description = c.Description
        });

在C#中,您有很多选项来处理如何处理这样的DTO的创建,包括:

>带有手动映射的显式类型对象(如我的示例).
>使用映射工具(如AutoMapper)显式输入对象.
>动态对象.

平面设计

请注意,使用Elasticsearch并不是简单地将数据放入“索引”中.您需要从“文档”开始思考,并在尝试索引来自关系数据库的数据时意味着什么. Elasticsearch指南文章Data In, Data Out是一个开始阅读的好地方.另一篇名为Managing relations inside Elasticsearch的文章与您的情况特别相关:

At it’s heart, Elasticsearch is a flat hierarchy and trying to force relational data into it can be very challenging. Sometimes the best solution is to judiciously choose which data to denormalize, and where a second query to retrieve children is acceptable

点赞