Выбор свойств для построения динамических c SQL запросов - PullRequest
0 голосов
/ 05 апреля 2020

Мне действительно не нравится жестко кодировать названия свойств моих моделей. Так что я придумал этот код до сих пор. Мой код работает нормально, и он делает именно то, что я хочу, но безобразно. Я уверен, что это будет проблематично c в ближайшее время. Таким образом, любая помощь, чтобы улучшить это и заставить это работать должным образом, ценится. Я ищу лучший способ извлечь выбранные имена свойств без преобразования тела выражения в строку. Любые изменения в любой части этого класса меня устраивают. Даже изменяя использование, если я не кодирую жестко имена своих свойств.

Какой лучший способ извлечь имя выбранной свойства модели?

Вот мой код:

public class Selector<T> : IDisposable
{
    Dictionary<string, Func<T, object>> Selectors = new Dictionary<string, Func<T, object>>();
    public Selector(params Expression<Func<T, object>>[] Selector)
    {
        foreach (var select in Selector)
        {
            //string MemberName = CleanNamesUp(select.Body.ToString());
            //Func<T, object> NewSelector = select.Compile();
    #region Ugly Part 1

            Selectors.Add(CleanNamesUp(select.Body.ToString()), select.Compile());

    #endregion
        }
    }

    #region I am Doing This So I can Use Using(var sl = new Selector<T>())
    public void Dispose()
    {
        Selectors.Clear();
        Selectors = null;

    }
    #endregion

    #region Ugly Part 2
    private string CleanNamesUp(string nameStr)
    {
        string name = nameStr.Split('.')[1];
        if (name.Contains(","))
        {
            name = name.Split(',')[0];
        }
        return name;
    }
    #endregion


    public Dictionary<string, object> GetFields(T Item)
    {
        Dictionary<string,object> SetFieldList = new Dictionary<string, object>();

            foreach(var select in Selectors)
            {
               SetFieldList.Add( select.Key , select.Value(Item)); 
            }


        return SetFieldList;
    }

    public List<Dictionary<string, object>> GetFields(IEnumerable<T> Items)
    {

        List<Dictionary<string, object>> SetFieldListMain = new List<Dictionary<string, object>>();

        foreach (var item in Items)
        {
            Dictionary<string, object> SetFieldList = new Dictionary<string, object>();

            foreach (var select in Selectors)
            {
                SetFieldList.Add(select.Key, select.Value(item));
            }

            SetFieldListMain.Add( SetFieldList);
        }

        return SetFieldListMain;
    }

    internal List<string> GetKeys()
    {
        return new List<string>(this.Selectors.Keys);
    }
}

Это моя модель:

public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public bool IsEnabled { get; set; }
    public bool IsLocked { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime LockedAt { get; set; }
}

И я использую это так:

User user1 = new User();
        user1.Email = "testDev@gmail.com";
        user1.UserName = "dora";
        user1.Password = "123456";
var UpObject = new Selector<User>( x => x.UserName, x => x.Password, x => x.Email, x => x.IsEnabled );
Dictionary<string,object> result = UpObject.GetFields(user1);

Ответы [ 2 ]

1 голос
/ 05 апреля 2020

Думаю, вы забыли важное ключевое слово nameof. С ключевым словом код будет выглядеть следующим образом:

class User
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string Tel { get; set; }
}

static Dictionary<string, object> GetFieldsOf<T>(T item, params string[] args)
{
    var properties = args.Select(property => typeof(T).GetProperty(property));
    return properties.ToDictionary(property => property.Name, property => property.GetValue(item));
}

static void Main(string[] args)
{
    var user = new User { Name = "Abel", Address = "Unknown", Tel = "XXX-XXX" };
    var result = GetFieldsOf(user, nameof(User.Name), nameof(User.Address));
}

Этот код приведет к некоторым проблемам с производительностью, вызванным отражением. Но, к счастью, вы можете избежать этого, испуская небольшой фрагмент кода.

//MSIL
ldarg.0
call Property.GetMethod
ret

И замените его на proerpty.GetValue. Этот код может быть сгенерирован и кэширован для каждого типа, что все еще стоит.

1 голос
/ 05 апреля 2020

Вы можете избежать синтаксического анализа выражений как строк, если вместо этого проанализируете их как System.Linq.Expressions.

Ниже приведен пример полного кода, но не совсем для вашего кода, я использовал DateTime вместо generi c T, адаптация должна быть просто найдена и заменена:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ExprTest
{
    class Program
    {
        static void Main(string[] args)
        {
            #region Usage
            Expression<Func<DateTime, object>> propertySelector = x => x.Day;
            Expression<Func<DateTime, object>> methodSelector = x => x.AddDays(1.5);

            Expression[] inputSelectors = new Expression[] { propertySelector, methodSelector };
            #endregion

            //These are your final Selectors
            Dictionary<string, Func<DateTime, object>> outputSelectors = new Dictionary<string, Func<DateTime, object>>();

            //This would be in your Selector<T> constructor, replace DateTime with T.
            //Instead of CleanNamesUp you would decide which part to use by extracting the appropriate Expression argument's Name.
            foreach (Expression<Func<DateTime, object>> selectorLambda in inputSelectors)
            {
                Expression selectorExpression = selectorLambda.Body;
                string name = null;
                while (string.IsNullOrEmpty(name))
                {
                    switch (selectorExpression)
                    {
                        #region Refine expression

                        //Necessary for value typed arguments, which get boxed by Convert(theStruct, object)
                        case UnaryExpression unary:
                            selectorExpression = unary.Operand;
                            break;

                        //add other required expression extractions

                        #endregion

                        #region Select expression key/name

                        case MemberExpression fieldOrProperty:
                            name = fieldOrProperty.Member.Name;
                            break;
                        case MethodCallExpression methodCall:
                            name = methodCall.Method.Name;
                            break;

                            //add other supported expressions

                            #endregion
                    }
                }
                outputSelectors.Add(name, selectorLambda.Compile());
            }

            //Set a breakpoint here to explore the outputSelectors
        }
    }
}

Для этого может быть библиотека, но я не знаю ни о чем, кроме PredicateBuilder для случаев, когда вам нужно объединить лямбду аргументы в одном лямбда-выражении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...