我目前正在实现一个用于缓存的 MongoDB 数据库。
我制作了一个非常通用的客户端,保存方法如下:
public virtual void SaveAndOverwriteExistingCollection<T>(string collectionKey, T[] data)
{
if (data == null || !data.Any())
return;
var collection = Connector.MongoDatabase.GetCollection<T>(collectionKey.ToString());
var filter = new FilterDefinitionBuilder<T>().Empty;
var operations = new List<WriteModel<T>>
{
new DeleteManyModel<T>(filter),
};
operations.AddRange(data.Select(t => new InsertOneModel<T>(t)));
try
{
collection.BulkWrite(operations, new BulkWriteOptions { IsOrdered = true});
}
catch (MongoBulkWriteException mongoBulkWriteException)
{
throw mongoBulkWriteException;
}
}
对于我们的其他客户,调用此方法看起来类似于:
public Person[] Get(bool bypassCache = false)
{
Person[] people = null;
if (!bypassCache)
people = base.Get<Person>(DefaultCollectionKeys.People.CreateCollectionKey());
if (people.SafeAny())
return people;
people = Client<IPeopleService>.Invoke(s => s.Get());
base.SaveAndOverwriteExistingCollection(DefaultCollectionKeys.People.CreateCollectionKey(), people);
return people;
}
在我们将数据持久化到后端之后,我们通过调用 Get 方法并传递参数 true 从 MongoDB 重新加载缓存。所以我们重新加载所有数据。
这适用于大多数用例。但是考虑到我们如何为同一个应用程序使用 Web-garden 解决方案(多个进程),这会导致并发问题。如果我在另一个用户重新加载页面时保存并重新加载缓存,有时它会引发 E11000 重复键错误集合。
命令 createIndexes 失败:E11000 重复键错误收集:缓存。人员索引:Id_1_Name_1_Email_1 重复键:{:1,:“John Doe”,:“foo@bar.com”}。
考虑到这是一个运行多个 IIS 进程的网络花园,锁定不会有多大用处。考虑到批量写入应该是线程安全的,我有点困惑。我已经研究了更新数据,但是将我们的客户更改为特定类型并更新每个字段将花费太长时间,并且感觉像是不必要的工作。因此,我正在寻找一个非常通用的解决方案。
更新 删除了插入和删除。将其更改为 ReplaceOneModel 的集合。目前遇到的问题是仅保留集合中的最后一个元素。
public virtual void SaveAndOverwriteExistingCollection<T>(string collectionKey, T[] data)
{
if (data == null || !data.Any())
return;
var collection = Connector.MongoDatabase.GetCollection<T>(collectionKey.ToString());
var filter = new FilterDefinitionBuilder<T>().Empty;
var operations = new List<WriteModel<T>>();
operations.AddRange(data.Select(t => new ReplaceOneModel<T>(filter, t) { IsUpsert = true }));
try
{
collection.BulkWrite(operations, new BulkWriteOptions { IsOrdered = true });
}
catch (MongoBulkWriteException mongoBulkWriteException)
{
throw mongoBulkWriteException;
}
}
刚刚传入了一个包含 811 项的集合,执行此方法后只能在集合中找到最后一项。
持久化 DTO 的示例:
public class TranslationSetting
{
[BsonId(IdGenerator = typeof(GuidGenerator))]
public object ObjectId { get; set; }
public string LanguageCode { get; set; }
public string SettingKey { get; set; }
public string Text { get; set; }
}
有了这个索引:
string TranslationSettings()
{
var indexBuilder = new IndexKeysDefinitionBuilder<TranslationSetting>()
.Ascending(_ => _.SettingKey)
.Ascending(_ => _.LanguageCode);
return MongoDBClient.CreateIndex(DefaultCollectionKeys.TranslationSettings, indexBuilder);
}