MongoDB 和使用存储库模式时的大型数据集
MongoDB and Large Datasets when using a Repository pattern
好的,在工作中我们正在使用 MVC C# 和 MongoDB 开发一个系统。第一次开发时,我们认为遵循存储库模式可能是个好主意(真是麻烦!),这里的代码可以让您了解当前实现的内容。
MongoRepository class:
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>();
}
}
IRepository 接口class:
public interface IRepository { }
public interface IRepository<T> : IRepository
where T : IEntity
{
string StoreNamePrepend { get; set; }
string StoreNameAppend { get; set; }
IQueryable<T> SearchFor();
/* misc code */
}
然后使用 Ninject 实例化存储库,但如果没有它,它看起来像这样(只是为了使这个示例更简单):
MongoRepository<Client> clientCol = new MongoRepository<Client>();
这是用于搜索页面的代码,用于输入控制器操作,输出 JSON 供 table 读取数据表。请注意,以下使用 DynamicLinq,以便可以从字符串输入构建 linq:
tmpFinalList = clientCol
.SearchFor()
.OrderBy(tmpOrder) // tmpOrder = "ClientDescription DESC"
.Skip(Start) // Start = 99900
.Take(PageLength) // PageLength = 10
.ToList();
现在的问题是,如果集合有很多记录(准确地说是 99,905 条),如果字段中的数据不是很大,例如我们的关键字段是 5 个字符的固定长度字符串,则一切正常我可以使用此查询跳过并正常执行。但是,如果它类似于 ClientDescription 可以更长,我可以 'Sort' fine 和 'Take' fine from the front of query (i.e. Page 1) 但是,当我使用 Skip = 99900 & Take 翻页到末尾时= 10 它给出了以下内存错误:
An exception of type 'MongoDB.Driver.MongoCommandException' occurred
in MongoDB.Driver.dll but was not handled in user code
Additional information: Command aggregate failed: exception: Sort
exceeded memory limit of 104857600 bytes, but did not opt in to
external sorting. Aborting operation. Pass allowDiskUse:true to opt
in..
好的,我想这很容易理解。我在网上看了看,大多数建议都是使用聚合和 "allowDiskUse:true" 但是因为我在 IRepository 中使用 IQueryable 我无法开始使用 IAggregateFluent<> 因为你需要公开 MongoDB 相关 classes 到 IRepository,这将违反 IoC 原则。
有什么方法可以强制 IQueryable 使用它,或者有人知道我可以在不违反 IoC 原则的情况下访问 IAggregateFluent 的方法吗?
我感兴趣的一件事是为什么排序适用于第 1 页(Start = 0,Take = 10)但是当我搜索到最后时失败......当然所有东西都必须排序对我来说,为了能够获得第 1 页的物品,但不应该(开始 = 99900,Take = 10)只需要相同数量的 'sorting' 和 MongoDB 应该只给我最后一个5条左右的记录。为什么两种排序都完成后没有出现这个错误?
回答
好的,在@craig-wilson 的帮助下升级到最新版本的 MongoDB C# 驱动程序并更改 MongoRepository 中的以下内容将解决问题:
public IQueryable<T> SearchFor() {
return _collection.AsQueryable<T>(new AggregateOptions { AllowDiskUse = true });
}
我得到了一个 System.MissingMethodException,但这是由 MongoDB 驱动程序的其他副本也需要更新造成的。
从 IMongoCollection 创建 IQueryable 时,您可以传入允许您设置 AllowDiskUse 的 AggregateOptions。
好的,在工作中我们正在使用 MVC C# 和 MongoDB 开发一个系统。第一次开发时,我们认为遵循存储库模式可能是个好主意(真是麻烦!),这里的代码可以让您了解当前实现的内容。
MongoRepository class:
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>();
}
}
IRepository 接口class:
public interface IRepository { }
public interface IRepository<T> : IRepository
where T : IEntity
{
string StoreNamePrepend { get; set; }
string StoreNameAppend { get; set; }
IQueryable<T> SearchFor();
/* misc code */
}
然后使用 Ninject 实例化存储库,但如果没有它,它看起来像这样(只是为了使这个示例更简单):
MongoRepository<Client> clientCol = new MongoRepository<Client>();
这是用于搜索页面的代码,用于输入控制器操作,输出 JSON 供 table 读取数据表。请注意,以下使用 DynamicLinq,以便可以从字符串输入构建 linq:
tmpFinalList = clientCol
.SearchFor()
.OrderBy(tmpOrder) // tmpOrder = "ClientDescription DESC"
.Skip(Start) // Start = 99900
.Take(PageLength) // PageLength = 10
.ToList();
现在的问题是,如果集合有很多记录(准确地说是 99,905 条),如果字段中的数据不是很大,例如我们的关键字段是 5 个字符的固定长度字符串,则一切正常我可以使用此查询跳过并正常执行。但是,如果它类似于 ClientDescription 可以更长,我可以 'Sort' fine 和 'Take' fine from the front of query (i.e. Page 1) 但是,当我使用 Skip = 99900 & Take 翻页到末尾时= 10 它给出了以下内存错误:
An exception of type 'MongoDB.Driver.MongoCommandException' occurred in MongoDB.Driver.dll but was not handled in user code
Additional information: Command aggregate failed: exception: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in..
好的,我想这很容易理解。我在网上看了看,大多数建议都是使用聚合和 "allowDiskUse:true" 但是因为我在 IRepository 中使用 IQueryable 我无法开始使用 IAggregateFluent<> 因为你需要公开 MongoDB 相关 classes 到 IRepository,这将违反 IoC 原则。
有什么方法可以强制 IQueryable 使用它,或者有人知道我可以在不违反 IoC 原则的情况下访问 IAggregateFluent 的方法吗?
我感兴趣的一件事是为什么排序适用于第 1 页(Start = 0,Take = 10)但是当我搜索到最后时失败......当然所有东西都必须排序对我来说,为了能够获得第 1 页的物品,但不应该(开始 = 99900,Take = 10)只需要相同数量的 'sorting' 和 MongoDB 应该只给我最后一个5条左右的记录。为什么两种排序都完成后没有出现这个错误?
回答
好的,在@craig-wilson 的帮助下升级到最新版本的 MongoDB C# 驱动程序并更改 MongoRepository 中的以下内容将解决问题:
public IQueryable<T> SearchFor() {
return _collection.AsQueryable<T>(new AggregateOptions { AllowDiskUse = true });
}
我得到了一个 System.MissingMethodException,但这是由 MongoDB 驱动程序的其他副本也需要更新造成的。
从 IMongoCollection 创建 IQueryable 时,您可以传入允许您设置 AllowDiskUse 的 AggregateOptions。