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

时间:2015-02-10 15:36:19

标签: c# entity-framework elasticsearch asp.net-mvc-5 nest

我已经获得了一个数据库,可以根据该数据库进行基本的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; }
    }

1 个答案:

答案 0 :(得分:2)

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

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

<强> 1。有选择地序列化大对象

JSON.NET的作者的

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

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

<强> 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的创建,包括:

  1. 带有手动映射的显式类型对象(如我的示例)。
  2. 使用AutoMapper等映射工具显式输入对象。
  3. 动态对象。
  4. 平面设计

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

      

    在它的核心,Elasticsearch是一个扁平的层次结构,并试图强制关系数据进入它可能是非常具有挑战性的。有时最好的解决方案是明智地选择要反规范化的数据,以及可以接受第二个检索子项的查询