.net平台的MongoDB使用(二)

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: .net平台的MongoDB使用(二)

实体更新封装


通过ID作为过滤条件更新整个实体在实际工作中是常有的。既然通过ID作为条件,那么只能通过UpdateOneAsync进行约束更新一条数据。更新的字段可以通过反射实体对象进行遍历属性。


下边是实现代码:


/// <summary>
    /// mongodb扩展方法
    /// </summary>
    internal static class MongoDbExtension
    {
        /// <summary>
        /// 获取更新信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        internal static UpdateDefinition<T> GetUpdateDefinition<T>(this T entity)
        {
            var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            var updateDefinitionList = GetUpdateDefinitionList<T>(properties, entity);
            var updateDefinitionBuilder = new UpdateDefinitionBuilder<T>().Combine(updateDefinitionList);
            return updateDefinitionBuilder;
        }
        /// <summary>
        /// 获取更新信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyInfos"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        internal static List<UpdateDefinition<T>> GetUpdateDefinitionList<T>(PropertyInfo[] propertyInfos, object entity)
        {
            var updateDefinitionList = new List<UpdateDefinition<T>>();
            propertyInfos = propertyInfos.Where(a => a.Name != "_id").ToArray();
            foreach (var propertyInfo in propertyInfos)
            {
                if (propertyInfo.PropertyType.IsArray || typeof(IList).IsAssignableFrom(propertyInfo.PropertyType))
                {
                    var value = propertyInfo.GetValue(entity) as IList;
                    var filedName = propertyInfo.Name;
                    updateDefinitionList.Add(Builders<T>.Update.Set(filedName, value));
                }
                else
                {
                    var value = propertyInfo.GetValue(entity);
                    if (propertyInfo.PropertyType == typeof(decimal))
                        value = value.ToString();
                    var filedName = propertyInfo.Name;
                    updateDefinitionList.Add(Builders<T>.Update.Set(filedName, value));
                }
            }
            return updateDefinitionList;
        }
    }


Lambda表达式更新封装


曾经用过其他ORM都清楚Lambda表达式使用是非常频繁的,MongoDB.Driver已经支持Lambda表达式的过滤条件,但没支持部分字段更新,因此由我们自己来写解析。


下边是现实代码:

 

#region Mongo更新字段表达式解析
    /// <summary>
    /// Mongo更新字段表达式解析
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MongoDbExpression<T> : ExpressionVisitor
    {
        #region 成员变量
        /// <summary>
        /// 更新列表
        /// </summary>
        internal List<UpdateDefinition<T>> UpdateDefinitionList = new List<UpdateDefinition<T>>();
        private string _fieldname;
        #endregion
        #region 获取更新列表
        /// <summary>
        /// 获取更新列表
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static List<UpdateDefinition<T>> GetUpdateDefinition(Expression<Func<T, T>> expression)
        {
            var mongoDb = new MongoDbExpression<T>();
            mongoDb.Resolve(expression);
            return mongoDb.UpdateDefinitionList;
        }
        #endregion
        #region 解析表达式
        /// <summary>
        /// 解析表达式
        /// </summary>
        /// <param name="expression"></param>
        private void Resolve(Expression<Func<T, T>> expression)
        {
            Visit(expression);
        }
        #endregion
        #region 访问对象初始化表达式
        /// <summary>
        /// 访问对象初始化表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMemberInit(MemberInitExpression node)
        {
            var bingdings = node.Bindings;
            foreach (var item in bingdings)
            {
                var memberAssignment = (MemberAssignment)item;
                _fieldname = item.Member.Name;
                if (memberAssignment.Expression.NodeType == ExpressionType.MemberInit)
                {
                    var lambda = Expression.Lambda<Func<object>>(Expression.Convert(memberAssignment.Expression, typeof(object)));
                    var value = lambda.Compile().Invoke();
                    UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
                }
                else
                {
                    Visit(memberAssignment.Expression);
                }
            }
            return node;
        }
        #endregion
        #region 访问二元表达式
        /// <summary>
        /// 访问二元表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression node)
        {
            UpdateDefinition<T> updateDefinition;
            var value = ((ConstantExpression)node.Right).Value;
            if (node.Type == typeof(int))
            {
                var realValue = (int)value;
                if (node.NodeType == ExpressionType.Decrement)
                    realValue = -realValue;
                updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
            }
            else if (node.Type == typeof(long))
            {
                var realValue = (long)value;
                if (node.NodeType == ExpressionType.Decrement)
                    realValue = -realValue;
                updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
            }
            else if (node.Type == typeof(double))
            {
                var realValue = (double)value;
                if (node.NodeType == ExpressionType.Decrement)
                    realValue = -realValue;
                updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
            }
            else if (node.Type == typeof(decimal))
            {
                var realValue = (decimal)value;
                if (node.NodeType == ExpressionType.Decrement)
                    realValue = -realValue;
                updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
            }
            else if (node.Type == typeof(float))
            {
                var realValue = (float)value;
                if (node.NodeType == ExpressionType.Decrement)
                    realValue = -realValue;
                updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
            }
            else
            {
                throw new Exception(_fieldname + "不支持该类型操作");
            }
            UpdateDefinitionList.Add(updateDefinition);
            return node;
        }
        #endregion
        #region 访问数组表达式
        /// <summary>
        /// 访问数组表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitNewArray(NewArrayExpression node)
        {
            var listLambda = Expression.Lambda<Func<IList>>(node);
            var list = listLambda.Compile().Invoke();
            UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, list));
            return node;
        }
        /// <summary>
        /// 访问集合表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitListInit(ListInitExpression node)
        {
            var listLambda = Expression.Lambda<Func<IList>>(node);
            var list = listLambda.Compile().Invoke();
            UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, list));
            return node;
        }
        #endregion
        #region 访问常量表达式
        /// <summary>
        /// 访问常量表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitConstant(ConstantExpression node)
        {
            var value = node.Type.IsEnum ? (int)node.Value : node.Value;
            UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
            return node;
        }
        #endregion
        #region 访问成员表达式
        /// <summary>
        /// 访问成员表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Type.GetInterfaces().Any(a => a.Name == "IList"))
            {
                var lambda = Expression.Lambda<Func<IList>>(node);
                var value = lambda.Compile().Invoke();
                UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
            }
            else
            {
                var lambda = Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object)));
                var value = lambda.Compile().Invoke();
                if (node.Type.IsEnum)
                    value = (int)value;
                UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
            }
            return node;
        }
        #endregion
    }
    #endregion


表达式树的解析


对于Lambda表达式的封装,我侧重讲一下。假如有一段这样的更新代码:


new MongoDbService().Update<User>(a => a._id == "d99ce40d7a0b49768b74735b91f2aa75", a => new User
            {
                AddressList = new List<string>
                {
                    "number1",
                    "number2"
                },
                Age = 10,
                BirthDateTime = DateTime.Now,
                Name = "skychen",
                NumList = new List<int>
                {
                    1211,23344
                },
                Sex = Sex.Woman,
                Son = new User
                {
                    Name = "xiaochenpi",
                    Age = 1
                }
            });


那么,我们可以调试监视看看(下图),我们可以得出两个重要信息:


1.Expression<Func<T, T>>解析出来Body的NodeType是MemberInit


2.Bindings里有需要修改的字段信息。


image.png

再调试进去看看Bindings的第一项,我们又可以了解了几个重要信息。


1.Bindings里的元素是MemberAssignment类型。


2.Member能取到Name属性,也就是字段名


3.Expression属性,使用 Expression.Lambda,进行Compile().Invoke()就能得到我们需要的值。


fileName和Value都能取到了,那么更新自然能解决了。


image.png


上图是源码的部分核心代码,奇怪的是,我并没有在VisitMemberInit里进行遍历Bindings后进行Update.Set,而是将item的Expression属性再一次访问。那是因为我需要针对不同的数据类型进行处理。


例如:常量,我可以定义一个object value进行去接收,如果遇到枚举我需要强转成整型。


集合与数组,假如草率的使用object类型,object value = Expression.Lambda<Func<object>>(node).Compile().Invoke(),那么更新到MongoDB里就会有bug,奇怪的_t,_v就会出现。以此我需要定义为IList才能解决这个问题。


此外,工作中还会遇到金额或者数量自增的情况。Amount = a.Amount+9.9M,Count =a.Count-1。 MongoDB.Driver提供了Builders<T>.Update.Inc方法,因此重写二元表达式进行封装。


image.png


附加


经过测试,官方驱动2.4.3和2.4.4版本对类型IList支持有问题,如下图,所以现在封装版本最高支持到2.4.2。



  image.png

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
1月前
|
存储 NoSQL Unix
.NET生成MongoDB中的主键ObjectId
.NET生成MongoDB中的主键ObjectId
.NET生成MongoDB中的主键ObjectId
|
6月前
|
NoSQL 关系型数据库 MySQL
Linux平台下安装部署MongoDB数据库及基本使用(1)
Linux平台下安装部署MongoDB数据库及基本使用(1)
253 0
|
5月前
|
开发框架 NoSQL .NET
.NET Core MongoDB的简单使用
.NET Core MongoDB的简单使用
29 0
|
29天前
|
数据采集 存储 监控
.NET智慧手术室管理平台源码
术前访视记录单、手术风险评估表、手术安全核查表、自费药品或耗材、麻醉知情同意书、麻醉记录单、分娩镇痛记录单、麻醉复苏单、术后镇痛记录单、术后访视记录单、压伤风险评估量表、手术清点记录单、护理记录单、输血护理记录单。
30 0
|
1月前
深入.net平台的分层开发
深入.net平台的分层开发
57 0
|
4月前
|
Web App开发 开发框架 .NET
asp.net基于WEB层面的云LIS系统平台源码
结合当今各检验科管理及实验室规模的不同状况,充分吸收当今IT科技的最新成就,开发出以高度产品化、功能强大、极易实施操作、并不断升级换代为主要特点的LIS系统。彻底解决检验科的信息孤岛,全面实现全院信息互通互联、高度共享,并为检验科的规范化管理提供了有力工具。
41 0
|
4月前
|
Web App开发 开发框架 .NET
asp.net基于WEB层面的区域云LIS系统平台源码
asp.net基于WEB层面的区域云LIS系统平台源码
49 1
|
4月前
|
开发框架 小程序 数据可视化
基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
|
5月前
|
存储 前端开发 程序员
一款基于.Net开发、开源、支持多平台云存储文件管理器
一款基于.Net开发、开源、支持多平台云存储文件管理器
42 0
|
5月前
|
数据采集 自然语言处理 监控
基于.Net开发的、支持多平台、多语言餐厅点餐系统
基于.Net开发的、支持多平台、多语言餐厅点餐系统
76 0