我有一个简单的需求generate string ID if field is null before inserting
。如果属性具有名称Id
,则它可以正常工作,但是它没有。
我有以下课程:
public abstract class CampaignBase
{
[BsonId(IdGenerator = typeof(StringObjectIdGenerator))]
[BsonRepresentation(BsonType.ObjectId)]
public string CampaignId { get; set; }
}
public class Campaign : CampaignBase {}
现在当我在数据库中插入MyData
时,我得到null
而不是生成的id。似乎这些属性并未应用,因为如果属性具有Id
名称,那么如果工作正常且属性可以更改实际数据布局(string
/ objectid
/ etc)。
这是我保存它的方式:
campaign
和campaignBase
引用同一个对象,所以不要介意。
UpdateOptions:
protected static UpdateOptions UpdateOptions => new UpdateOptions
{
IsUpsert = true
};
这就是:null到了:
我错过了什么吗?
答案 0 :(得分:1)
我只是将命令分解为它正在做的事情,而不是试图混合问题:
var myItem = new MyItem() {Name = "Bob"};
if (myItem.MyId == null)
{
mongoCollection.InsertOne(myItem);
}
else
{
mongoCollection.ReplaceOne(x => x.MyId == myItem.MyId, myItem);
}
替换null
的ID只会将null作为文档的_id
插入。
答案 1 :(得分:0)
我最终得到了以下扩展名:
public static void Save<T, TProperty>(this IMongoCollection<T> collection, T item, Expression<Func<T, TProperty>> idFunc) where TProperty : class
{
var id = idFunc.Compile()(item);
if (id == null)
{
collection.InsertOne(item);
}
else
{
var expression = Expression.Lambda<Func<T, bool>>(Expression.Equal(idFunc.Body, Expression.Constant(id, typeof(TProperty))), idFunc.Parameters);
collection.ReplaceOne(expression, item);
}
}
样本用法:
CampaignsCollection.Save(campaign, c => c.CampaignId);
感谢@KevinSmith的想法
这是一个更复杂的实现,但它更安静,它具有更好的客户端界面和缓存性能
public static class MongoExtensions
{
public static void Save<T>(this IMongoCollection<T> collection, T item)
{
if (item == null)
throw new ArgumentNullException(nameof(item));
if (MongoSaveCommandHelper<T>.ShouldInsert(item))
{
collection.InsertOne(item);
}
else
{
var expression = MongoSaveCommandHelper<T>.GetIdEqualityExpression(item);
collection.ReplaceOne(expression, item);
}
}
private static class MongoSaveCommandHelper<T>
{
private static readonly Expression<Func<T, bool>> IdIsEqualToDefaultExpression;
private static readonly Func<T, object> GetId;
public static Func<T, bool> ShouldInsert { get; }
static MongoSaveCommandHelper()
{
var members = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
var idProperty = members.SingleOrDefault(x => x.IsDefined(typeof(BsonIdAttribute)))
?? members.FirstOrDefault(m => m.Name.Equals("id", StringComparison.OrdinalIgnoreCase));
if (idProperty == null)
throw new InvalidOperationException("Id property has not found");
var idPropertyType = idProperty.PropertyType;
var parameter = Expression.Parameter(typeof(T));
var idPropertyAccess = Expression.MakeMemberAccess(parameter, idProperty);
var getIdFuncExpression = Expression.Lambda<Func<T, object>>(Expression.Convert(idPropertyAccess, typeof(object)), parameter);
GetId = getIdFuncExpression.Compile();
IdIsEqualToDefaultExpression = Expression.Lambda<Func<T, bool>>(Expression.Equal(idPropertyAccess, Expression.Default(idPropertyType)), getIdFuncExpression.Parameters);
ShouldInsert = IdIsEqualToDefaultExpression.Compile();
}
public static Expression<Func<T, bool>> GetIdEqualityExpression(T item) =>
(Expression<Func<T, bool>>)new IdConstantVisitor(GetId(item)).Visit(IdIsEqualToDefaultExpression);
}
private class IdConstantVisitor : ExpressionVisitor
{
private readonly object _value;
public IdConstantVisitor(object value) => _value = value;
protected override Expression VisitDefault(DefaultExpression node) => Expression.Constant(_value, node.Type);
}
}
通常情况下,如果Id
字段等于default(PropertyType)
,则只插入一个项目,否则它会替换具有指定ID的项目。
以下是我们如何使用它:
CampaignsCollection.Save(campaign);
此代码处理我们的一切。没有错误的无效ID列,没有额外的输入,只需保存,期间:)