Сериализация выражения с переменной - PullRequest
3 голосов
/ 09 декабря 2011

Я написал несколько классов для сериализации System.Linq.Expressions в DataContracts для возможности отправки через WCF. Это работает довольно хорошо, приятно. проблема в том, когда я хочу сериализовать выражение, в котором есть переменная. Вот пример, чтобы объяснить проблему:

public class Foo
{
    public string Name { get; set; }
}

// CASE 1
Expression<Func<Foo, bool>> lambda = foo => foo.Name == "Test";
Console.WriteLine(lambda);
// OUTPUT: foo => (foo.Name == "Test")

// CASE 2
var variable = "Test";
lambda = foo => foo.Name == variable;            
this.AssertExpression(lambda, "Class Lambda expression with variable.");
// OUTPUT: foo => (foo.Name == value(MyTest+<>c__DisplayClass0).variable)

У меня нет проблем с сериализацией выражения CASE 2, но данные, которые я сериализирую, бесполезны, так как на стороне службы нет ничего, что нужно разрешить value(MyTest+<>c__DisplayClass0).variable

поэтому мне нужно разрешить переменные перед сериализацией этого выражения, чтобы выражение CASE 2 сериализовалось с тем же результатом, что и CASE1

1 Ответ

2 голосов
/ 09 декабря 2011

Извините за VB, но следующий фрагмент кода - фрагмент кода, который я имел в виду в своем комментарии. Я не думаю, что он охватывает все базы (то есть, возможно, он недостаточно детализирован, поэтому обязательно проверьте его) , но для простых в большинстве примеров это работает:

Код основан на этом примере посетителя выражения MSDN :

class CustomExpressionWalker<TSource> : ExpressionVisitor
{
    protected override Expression VisitMemberAccess(MemberExpression m)
    {
        if (m.Member.DeclaringType != typeof(TSource))
        {
            // We are accessing a member/variable on a class
            // We need to descend the tree through the navigation properties and eventually derive a constant expression
            return this.VisitMemberAccess(m, m.Type);
        }
        throw new NotImplementedException();
    }

    protected Expression VisitMemberAccess(MemberExpression m, Type expectedType)
    {
        if (m.Expression.NodeType == ExpressionType.Constant)
        {
            // We are at the end of the member expression 
            // i.e. MyClass.Something.Something.Value <-- we're at the Value part now
            ConstantExpression constant = (ConstantExpression)m.Expression;
            return Expression.Constant(m.Expression.Type.GetFields().Single(n => n.FieldType == expectedType && m.Member.Name.Contains(n.Name)).GetValue(constant.Value));
        }
        else if (m.Member.DeclaringType == typeof(TSource))
        {
            // I'm unsure of your current implementation but the original Member access
            // regarding serializing the expression, but if the code reaches here a nested
            // MemberAccess has landed on a Property/variable of type TSource, so you'll need
            // to decide whether to serialize here or not.  For example, if TSource was of 
            // type "myClass", it could be 
            // (myOtherClass x) => x.myClass
            throw new NotImplementedException();
        }
        else if (m.Member.DeclaringType == typeof(Nullable))
        {
            // never got round to implementing this as we don't need it yet
            // if you want to deal with Nullable<T> you're going to have to 
            // examine the logic here
            throw new NotImplementedException();
        }
        else
        {
            // continue walking the member access until we derive the constant
            return this.VisitMemberAccess((MemberExpression)m.Expression, expectedType);
        }
    }
}   

Надеюсь, это поможет!

РЕДАКТИРОВАТЬ: Первоначальная проблема, с которой я столкнулся , заключалась в том, что я не продолжал ходить по дереву, когда MemberAccess был не TSource классом, приведенная выше логика должна фактически рекурсивно искоренить эти случаи, поэтому игнорируйте мои оригинальный комментарий. Я оставил в предложении Nullable<T> (в выражении else if) , поскольку я не думаю, что существующая логика будет охватывать эти случаи, она также может бороться с Generic классы.

Тем не менее, это должно помочь вам. Если вы не используете Expression Visitor, можете ли вы предоставить более подробную информацию / код?

Удачи!

...