MongoDb id生成器无法正常工作

时间:2017-12-14 15:54:17

标签: c# mongodb mongodb-.net-driver

我有一个简单的需求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)。

这是我保存它的方式:

enter image description here

campaigncampaignBase引用同一个对象,所以不要介意。

UpdateOptions:

protected static UpdateOptions UpdateOptions => new UpdateOptions
{
    IsUpsert = true
};

这就是:null到了:

enter image description here

我错过了什么吗?

2 个答案:

答案 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列,没有额外的输入,只需保存,期间:)