简体   繁体   English

使用存储库模式时的 MongoDB 和大型数据集

[英]MongoDB and Large Datasets when using a Repository pattern

Okay so at work we are developing a system using MVC C# & MongoDB.好的,在工作中,我们正在使用 MVC C# 和 MongoDB 开发系统。 When first developing we decided it would probably be a good idea to follow the Repository pattern (what a pain in the ass!), here is the code to give an idea of what is currently implemented.在第一次开发时,我们决定遵循 Repository 模式可能是个好主意(这真是太痛苦了!),这里是给出当前实现内容的代码。

The MongoRepository class: MongoRepository 类:

public class MongoRepository { }

public class MongoRepository<T> : MongoRepository, IRepository<T>
where T : IEntity
{
    private MongoClient _client;
    private IMongoDatabase _database;
    private IMongoCollection<T> _collection;

    public string StoreName {
        get {
                return typeof(T).Name;
            }
        }
    }

    public MongoRepository() {

        _client = new MongoClient(ConfigurationManager.AppSettings["MongoDatabaseURL"]);
        _database = _client.GetDatabase(ConfigurationManager.AppSettings["MongoDatabaseName"]);

        /* misc code here */

        Init();
    }

    public void Init() {
        _collection = _database.GetCollection<T>(StoreName);
    }

    public IQueryable<T> SearchFor() {
        return _collection.AsQueryable<T>();
    }
}

The IRepository interface class: IRepository 接口类:

public interface IRepository { }

public interface IRepository<T> : IRepository
where T : IEntity
{

    string StoreNamePrepend { get; set; }

    string StoreNameAppend { get; set; }

    IQueryable<T> SearchFor();

    /* misc code */

}

The repository is then instantiated using Ninject but without that it would look something like this (just to make this a simpler example):然后使用 Ninject 实例化存储库,但如果没有它,它将看起来像这样(只是为了使它成为一个更简单的示例):

MongoRepository<Client> clientCol = new MongoRepository<Client>();

Here is the code used for the search pages which is used to feed into a controller action which outputs JSON for a table with DataTables to read.这是用于搜索页面的代码,用于输入控制器操作,该操作输出 JSON 以供读取带有 DataTables 的表。 Please note that the following uses DynamicLinq so that the linq can be built from string input:请注意,以下使用 DynamicLinq 以便可以从字符串输入构建 linq:

tmpFinalList = clientCol
    .SearchFor()
    .OrderBy(tmpOrder) // tmpOrder = "ClientDescription DESC"
    .Skip(Start) // Start = 99900
    .Take(PageLength) // PageLength = 10
    .ToList();

Now the problem is that if the collection has a lot of records (99,905 to be exact) everything works fine if the data in a field isn't very large for example our Key field is a 5 character fixed length string and I can Skip and Take fine using this query.现在的问题是,如果集合有很多记录(准确地说是 99,905),如果字段中的数据不是很大,则一切正常,例如我们的 Key 字段是一个 5 个字符的固定长度字符串,我可以跳过和使用这个查询就好了。 However if it is something like ClientDescription can be much longer I can 'Sort' fine and 'Take' fine from the front of the query (ie Page 1) however when I page to the end with Skip = 99900 & Take = 10 it gives the following memory error:但是,如果它是像 ClientDescription 这样的东西可以更长,我可以从查询的前面(即第 1 页)“排序”和“获取”很好,但是当我用 Skip = 99900 & Take = 10 翻页到最后时,它给出以下内存错误:

An exception of type 'MongoDB.Driver.MongoCommandException' occurred in MongoDB.Driver.dll but was not handled in user code MongoDB.Driver.dll 中发生类型为“MongoDB.Driver.MongoCommandException”的异常,但未在用户代码中处理

Additional information: Command aggregate failed: exception: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting.附加信息:命令聚合失败:异常:排序超出了 104857600 字节的内存限制,但没有选择外部排序。 Aborting operation.中止操作。 Pass allowDiskUse:true to opt in..通过 allowDiskUse:true 选择加入..

Okay so that is easy to understand I guess.好的,我想这很容易理解。 I have had a look online and mostly everything that is suggested is to use Aggregation and "allowDiskUse:true" however since I use IQueryable in IRepository I cannot start using IAggregateFluent<> because you would then need to expose MongoDB related classes to IRepository which would go against IoC principals.我在网上看过,大部分建议是使用聚合和“allowDiskUse:true”但是因为我在 IRepository 中使用 IQueryable 我不能开始使用 IAggregateFluent<> 因为你需要将 MongoDB 相关类公开给 IRepository 这将违背国际奥委会原则。

Is there any way to force IQueryable to use this or does anyone know of a way for me to access IAggregateFluent without going against IoC principals?有什么方法可以强制 IQueryable 使用它,或者有没有人知道我可以在不违反 IoC 原则的情况下访问 IAggregateFluent 的方法?

One thing of interest to me is why the sort works for page 1 (Start = 0, Take = 10) but then fails when I search to the end ... surely everything must be sorted for me to be able to get the items in order for Page 1 but shouldn't (Start = 99900, Take = 10) just need the same amount of 'sorting' and MongoDB should just send me the last 5 or so records.我感兴趣的一件事是为什么排序适用于第 1 页(开始 = 0,取值 = 10)但是当我搜索到最后时失败......当然,所有东西都必须排序才能让我能够获取项目订购第 1 页但不应该(开始 = 99900,Take = 10)只需要相同数量的“排序”,MongoDB 应该只向我发送最后 5 条左右的记录。 Why doesn't this error happen when both sorts are done?为什么两种类型都完成后不会发生此错误?


ANSWER回答

Okay so with the help of @craig-wilson upgrading to the newest version of MongoDB C# drivers and changing the following in MongoRepository will fix the problem:好的,在@craig-wilson 的帮助下升级到最新版本的 MongoDB C# 驱动程序并在 MongoRepository 中更改以下内容将解决问题:

public IQueryable<T> SearchFor() {
    return _collection.AsQueryable<T>(new AggregateOptions { AllowDiskUse = true });
}

I was getting a System.MissingMethodException but this was caused by other copies of the MongoDB drivers needing updated as well.我收到了 System.MissingMethodException ,但这是由 MongoDB 驱动程序的其他副本也需要更新引起的。

When creating the IQueryable from an IMongoCollection, you can pass in the AggregateOptions which allow you to set AllowDiskUse.从 IMongoCollection 创建 IQueryable 时,您可以传入允许您设置 AllowDiskUse 的 AggregateOptions。

https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver/IMongoCollectionExtensions.cs#L53 https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver/IMongoCollectionExtensions.cs#L53

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM