EFCore擴展Select方法(根據實體定制查詢語句)


EFCore擴展Select方法(根據實體定制查詢語句) 

通常用操作數據庫的時候查詢返回的字段是跟 我們的定義的實體是不一致的,所以往往針對UI或者接口層創建大量的Model, 而且需要手動對應字段,非常繁瑣。 本文將通過表達式樹解決這些重復的過程。 

 

先貼上實現代碼

  Queryable 類中 的擴展方法  Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)  需要參數 Expression<Func<TSource, TResult>> selector 只要構造相應的表達式樹即可實現自定義映射

    using System.Collections;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq.Expressions;
    using System.Reflection;
    using static System.Linq.Expressions.Expression;
    public static class QueryableExtentions
    {
        public static IQueryable<TTarget> Select<TTarget>(this IQueryable<object> query)
        {
            return Queryable.Select(query, GetLamda<object, TTarget>(query.GetType().GetGenericArguments()[0]));
        }

        public static IQueryable<TTarget> Select<TSource, TTarget>(this IQueryable<TSource> query)
        {
            return Queryable.Select(query, GetLamda<TSource, TTarget>());
        }

        public static Expression<Func<TSource, TTarget>> GetLamda<TSource, TTarget>(Type type = null)
        {
            var sourceType = typeof(TSource);
            var targetType = typeof(TTarget);
            var parameter = Parameter(sourceType);
            Expression propertyParameter;
            if (type != null)
            {
                propertyParameter = Convert(parameter, type);
                sourceType = type;
            }
            else
                propertyParameter = parameter;

            return Lambda<Func<TSource, TTarget>>(GetExpression(propertyParameter, sourceType, targetType), parameter);
        }

        public static MemberInitExpression GetExpression(Expression parameter, Type sourceType, Type targetType)
        {
            var memberBindings = new List<MemberBinding>();
            foreach (var targetItem in targetType.GetProperties().Where(x => x.CanWrite))
            {
                var fromEntityAttr = targetItem.GetCustomAttribute<FromEntityAttribute>();
                if (fromEntityAttr != null)
                {
                    var property = GetFromEntityExpression(parameter, sourceType, fromEntityAttr);
                    if (property != null)
                        memberBindings.Add(Bind(targetItem, property));
                    continue;
                }

                var sourceItem = sourceType.GetProperty(targetItem.Name);
                if (sourceItem == null)//當沒有對應的屬性時,查找 實體名+屬性
                {
                    var complexSourceItemProperty = GetCombinationExpression(parameter, sourceType, targetItem);
                    if (complexSourceItemProperty != null)
                        memberBindings.Add(Bind(targetItem, complexSourceItemProperty));
                    continue;
                }

                //判斷實體的讀寫權限
                if (sourceItem == null || !sourceItem.CanRead)
                    continue;

                //標注NotMapped特性的屬性忽略轉換
                if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
                    continue;

                var sourceProperty = Property(parameter, sourceItem);

                //當非值類型且類型不相同時
                if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType && targetItem.PropertyType != targetType)
                {
                    //判斷都是(非泛型、非數組)class
                    if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass
                        && !sourceItem.PropertyType.IsArray && !targetItem.PropertyType.IsArray
                        && !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
                    {
                        var expression = GetExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
                        memberBindings.Add(Bind(targetItem, expression));
                    }
                    continue;
                }

                if (targetItem.PropertyType != sourceItem.PropertyType)
                    continue;

                memberBindings.Add(Bind(targetItem, sourceProperty));
            }

            return MemberInit(New(targetType), memberBindings);
        }

        /// <summary>
        /// 根據FromEntityAttribute 的值獲取屬性對應的路徑
        /// </summary>
        /// <param name="sourceProperty"></param>
        /// <param name="sourceType"></param>
        /// <param name="fromEntityAttribute"></param>
        /// <returns></returns>
        private static Expression GetFromEntityExpression(Expression sourceProperty, Type sourceType, FromEntityAttribute fromEntityAttribute)
        {
            var findType = sourceType;
            var resultProperty = sourceProperty;
            var tableNames = fromEntityAttribute.EntityNames;
            if (tableNames == null)
            {
                var columnProperty = findType.GetProperty(fromEntityAttribute.EntityColuum);
                if (columnProperty == null)
                    return null;
                else
                    return Property(resultProperty, columnProperty);
            }

            for (int i = tableNames.Length - 1; i >= 0; i--)
            {
                var tableProperty = findType.GetProperty(tableNames[i]);
                if (tableProperty == null)
                    return null;

                findType = tableProperty.PropertyType;
                resultProperty = Property(resultProperty, tableProperty);
            }

            var property = findType.GetProperty(fromEntityAttribute.EntityColuum);
            if (property == null)
                return null;
            else
                return Property(resultProperty, property);
        }

        /// <summary>
        /// 根據組合字段獲取其屬性路徑
        /// </summary>
        /// <param name="sourceProperty"></param>
        /// <param name="sourcePropertys"></param>
        /// <param name="targetItem"></param>
        /// <returns></returns>
        private static Expression GetCombinationExpression(Expression sourceProperty, Type sourceType, PropertyInfo targetItem)
        {
            foreach (var item in sourceType.GetProperties().Where(x => x.CanRead))
            {
                if (targetItem.Name.StartsWith(item.Name))
                {
                    if (item != null && item.CanRead && item.PropertyType.IsClass && !item.PropertyType.IsGenericType)
                    {
                        var rightName = targetItem.Name.Substring(item.Name.Length);

                        var complexSourceItem = item.PropertyType.GetProperty(rightName);
                        if (complexSourceItem != null && complexSourceItem.CanRead)
                            return Property(Property(sourceProperty, item), complexSourceItem);
                    }
                }
            }

            return null;
        }
    }

    /// <summary>
    /// 用於標注字段 來自哪個表的的哪一列(僅限於有關聯的表中)
    /// </summary>
    public class FromEntityAttribute : Attribute
    {
        /// <summary>
        /// 類名(表名)
        /// </summary>
        public string[] EntityNames { get; }

        /// <summary>
        /// 字段(列名)
        /// </summary>
        public string EntityColuum { get; }

        /// <summary>
        /// 列名 + 該列的表名 + 該列的表的上一級表名
        /// </summary>
        /// <param name="entityColuum"></param>
        /// <param name="entityNames"></param>
        public FromEntityAttribute(string entityColuum, params string[] entityNames)
        {
            EntityNames = entityNames;
            EntityColuum = entityColuum;
        }
    }

 

調用方法如下,先構造測試類

    public partial class User
    {
        public int Id { get; set; }
        [Required]
        [StringLength(50)]
        public string Name { get; set; }
        public int RoleId { get; set; }

        [ForeignKey(nameof(RoleId))]
        public virtual Role Role { get; set; }
    }
    
    public partial class Role
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int DepartmentId { get; set; }

        [ForeignKey(nameof(DepartmentId))]
        public virtual Department Department  { get; set; } 
    }

    public partial class Department
    {
        public int Id { get; set; }
        [Required]
        [StringLength(50)]
        public string Name { get; set; }
    }

如上所以構造了,用戶表,角色表,和部門表。  查詢某個用戶 的角色名和部門名 則需要關聯 角色表和部門表

    public partial class UserModel
    {
        public string Name { get; set; }

        public string RoleName { get; set; }

        //[FromEntity("Name","Role")]
        //public string RoleName1 { get; set; }

        [FromEntity("Name", "Department", "Role")]
        public string DepartmentName { get; set; }

        //public virtual RoleModel Role { get; set; }

        //[FromEntity("Department", "Role")]
        //public virtual Department Department { get; set; }
    }

查詢代碼如下

static void Main(string[] args)
        {
            using (var context = new TestContext())
            {
                var list = context.User.Select<UserModel>().ToList();
            }
            Console.WriteLine($"------------結束--------------------");
            Console.ReadLine();
        }

生成的sql語句 如下圖

 

實體中的 DepartmentName 由於通過用戶表關聯角色表,再通過角色表關聯 部門表得到故 需要通過特性標注

當然結果實體也可以多級關聯

    public partial class UserModel
    {
        public string Name { get; set; }

        public string RoleName { get; set; }

        [FromEntity("Name","Role")]
        public string RoleName1 { get; set; }

        [FromEntity("Name", "Department", "Role")]
        public string DepartmentName { get; set; }

        public virtual RoleModel Role { get; set; }

        [FromEntity("Department", "Role")]
        public virtual Department Department { get; set; }
    }
    public partial class RoleModel
    {
        public string Name { get; set; }
        public string DepartmentName { get; set; }

        public virtual DepartmentModel Department  { get; set; } 
    }
    public partial class DepartmentModel
    {
        public string Name { get; set; }
    }

生成的查詢語句如下圖

 

總結 此方案用在接口,精確查詢字段,需要強類型視圖的地方相對比較方便

作者:costyuan

GitHub地址:https://github.com/bieyuan/EFCoreSelectExtentions

地址:https://www.cnblogs.com/castyuan/p/10186619.html
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 
如果文中有什么錯誤,歡迎指出,謝謝! 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM