Передача нескольких свойств класса в функцию и возврат значений настраиваемого атрибута - PullRequest
3 голосов
/ 10 июля 2020

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

Итак, мои классы домена выглядят примерно так:

[OhtherSystemObjectName("otherSystemAccountName")]
public class Account : SomeBaseClass
{
    [OhtherSystemAttributeName("otherSystsemAccountNameColumn")]
    public string Name { get; set; }

    [OhtherSystemAttributeName("otherSystsemAccountNumberColumn")]
    public string AccountNumber { get; set; }

    [OhtherSystemAttributeName("otherSystsemAddressColumn")]
    public string Address { get; set; }

    [OhtherSystemAttributeName("otherSystsemPostalCodeColumn")]
    public string PostalCode { get; set; }

}

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

public string[] GetColumnSet<T>(Expression<Func<T, object[]>> attributes) where T : SomeBaseClass
{
    
    return ((NewArrayExpression)attributes.Body).Expressions.Select(a =>
    {

        switch (a.NodeType)
        {
            case ExpressionType.MemberAccess:
                return GetAttributeName((MemberExpression)a);
            case ExpressionType.Convert:
                return GetAttributeName((MemberExpression)((UnaryExpression)a).Operand);

        }

        return null;

    }).Where(a => !string.IsNullOrEmpty(a)).ToArray();  
}

private static string GetAttributeName(MemberExpression memberExpression)
{
    var attribute = memberExpression.Member.GetCustomAttribute<OhtherSystemAttributeName>();
    return attribute?.AttributeName;
}

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

var colums = GetColumnSet<Account>(a => new object[] {a.Name, a.AccountNumber});

Нет, я бы хотел, чтобы это было более похоже:

var colums = GetColumnSet<Account>(a => {a.Name, a.AccountNumber});

Без new object[]{}, но Expression<Func<T, param object[]>> не скомпилировать.

Можно ли переписать входное выражение для выполнения sh синтаксиса, подобного этому?

Ответы [ 2 ]

2 голосов
/ 13 июля 2020

Вы не можете использовать тот же синтаксис, который вам нужен, но вы можете использовать анонимный объект:

var colums = GetColumnSet<Account>(a => new { a.Name, a.AccountNumber });

Вы можете реализовать метод следующим образом:

public string[] GetColumnSet<T>(Expression<Func<T, object>> selector) where T : SomeBaseClass
{
    return ((NewExpression)selector.Body)
        .Type
        .GetProperties()
        .Select(prop => typeof(T)
            .GetProperty(prop.Name)?
            .GetCustomAttribute<OtherSystemAttributeName>()?
            .AttributeName)
        .Where(name => !string.IsNullOrEmpty(name))
        .ToArray();
}

Любые имена свойств, указанные в вашем анонимном объекте, которых нет в вашем объекте T, или свойства, которые не украшены атрибутом OtherSystemAttributeName, просто игнорируются, что, по-видимому, будет вашим поведением после.

0 голосов
/ 13 июля 2020

Вы также можете использовать следующую функцию Dude():

public static void Dude(params object[] theListOfArguments) {}

Тогда вызов GetColumnSet будет выглядеть немного иначе и, на мой взгляд, лучше, чем new {...}

var colums = GetColumnSet<Account>(a => Dude(a.Name, a.AccountNumber));

Также требуется два незначительных изменения функции GetColumnSet.

  • Func<T,object[]> на Action<T> изменение типа параметра и
  • выражение, которое извлекает список полей из параметров действия . Это первая строка кода в функции.

Остальная часть кода такая же.

public static string[] GetColumnSet<T>(Expression<Action<T>> attributes) where T : SomeBaseClass
{
    return ((NewArrayExpression)((MethodCallExpression)attributes.Body).Arguments.First()).Expressions.Select(a =>
    {
        switch (a.NodeType)
        {
            case ExpressionType.MemberAccess:
                return GetAttributeName((MemberExpression)a);
            case ExpressionType.Convert:
                return GetAttributeName((MemberExpression)((UnaryExpression)a).Operand);
        }
        return null;
    }).Where(a => !string.IsNullOrEmpty(a)).ToArray();
}
...