【问题标题】:C# Expression Serialization / Parameter ReplacementC# 表达式序列化/参数替换
【发布时间】:2014-12-12 08:38:50
【问题描述】:

首先,简要概述一下我正在尝试做的事情:我想要获取一个 C# 表达式,对其进行序列化,将其发送到另一个进程,对其进行反序列化,然后使用它来过滤列表。这是警告 - 创建表达式时,它是针对通用参数类型 T 完成的,但是当它被反序列化时,它需要与动态相反。这样做的原因是,当它最终在不同进程中的服务器上使用时,它将针对动态列表执行此操作,因为在该上下文中我不会有类型信息。

我觉得我已经很接近了,因为我使用了 Newtonsoft.Json 和 Serialize.Linq 库来组合概念证明,但我只能在不使用动态的情况下让它工作(例如,我有可用的类型 T在序列化端(客户端)和反序列化端(服务器)。在一些代码上向你展示我所拥有的......

鉴于此:

public class Person
{
    public string Name { get; set; }
    public string Email { get; set; }
}

...作为我们正在使用的类型。我有一个有接口的客户端:

public interface IClient
{
    T Get<T>(Expression<Func<T, bool>> query);
}

...所以我可以这样做:

var client = new Client();
var john = client.Get<Person>(p => p.Name == "John");

...到目前为止一切都很好。在 client.Get() 方法中,我正在获取传递的表达式并将其序列化并发送到服务器。服务器如下所示:

 public dynamic Server(string serializedExpression)
 {
    var people = new List<dynamic>
    {
        new { Name = "John", Email = "john@stackoverflow.com" },
        new { Name = "Jane", Email = "jane@stackoverflow.com" }
    };

    var serializer = new ExpressionSerializer(new JsonSerializer());
    var deserializedExpression = (Expression<Func<dynamic, bool>>)serializer.DeserializeText(serializedExpression);

    return people.FirstOrDefault(deserializedExpression.Compile());
}

...这就是问题发生的地方,因为我试图将其反序列化为一种

Expression<Func<dynamic, bool>> 

...而不是...

Expression<Func<Person, bool>>.

所以我的问题是:

1) 我正在尝试做的事情是否可能?似乎使用 ExpressionVisitor 可以更改通用参数类型,我尝试这样做以在序列化和发送之前从 Person 更改为 dynamic,但没有运气。

2) 有没有更好的方法来做我想要完成的事情?我知道第一个问题是我为什么不直接访问服务器上表达式 Func 中指定的类型 T,但由于服务器的性质,这是不可能的。我基本上希望在客户端上使用 Linq 来指定查询谓词,同时针对动态列表执行这些谓词。

提前感谢您提供的任何答案或想法。

问候,

克雷格

【问题讨论】:

  • 可能是愚蠢的建议,但是通过拥有一些您知道所有内容都继承自的基本IMyObject 接口或类,是否有可能在服务器上“不太动态”?
  • 在尝试解决这个问题后的问题。您实际尝试过滤的列表是dynamics 的集合还是静态类型?
  • Nate - 不是一个愚蠢的建议,我曾想过这样做,但一旦我用 IMyObject 标记我的表达式,我就失去了在我的表达式中做有意义的 lambda 的能力(AFAIK) ' 将被限制为只对 IMyObject 成员进行操作,除非我做了一堆我想避免的强制转换。
  • 我应该指定我实际上是在谈论拥有一个实际的 Person 而不是匿名类型 (new {...}) 的集合。列表中的T 并不多。
  • Ty - 我想要实际应用表达式的服务器上的列表将是一个动态集合,因为我在该进程上下文中没有任何相关的类型信息。服务器不知道 Person 类型是什么,因为它的数据都是从 JSON 加载到动态中的。问题是服务器上的数据将匹配定义的 Person 类型,因此表达式应该针对这些动态正确操作,因为它们将具有客户端上 Person 类型定义的所有相同属性。这有意义吗?

标签: c# linq expression func


【解决方案1】:

LINQ doesn't like 动态表达式中的很多。 (也许是 LINQ?)

或者,您可以向服务器传递有关您正在使用的对象类型的提示。我意识到这可能不是您想要的,但它正在工作:

(借自this CodeProject article

public static class Extensions
{
    public static object ToType<T>(this object obj, T type)
    {

        //create instance of T type object:
        var tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

        //loop through the properties of the object you want to covert:          
        foreach (PropertyInfo pi in obj.GetType().GetProperties())
        {
            try
            {

                //get the value of property and try 
                //to assign it to the property of T type object:
                tmp.GetType().GetProperty(pi.Name).SetValue(tmp,
                                          pi.GetValue(obj, null), null);
            }
            catch { }
        }

        //return the T type object:         
        return tmp;
    }

    public static object ToNonAnonymousList<T>(this List<T> list, Type t)
    {

        //define system Type representing List of objects of T type:
        var genericType = typeof(List<>).MakeGenericType(t);

        //create an object instance of defined type:
        var l = Activator.CreateInstance(genericType);

        //get method Add from from the list:
        MethodInfo addMethod = l.GetType().GetMethod("Add");

        //loop through the calling list:
        foreach (T item in list)
        {

            //convert each object of the list into T object 
            //by calling extension ToType<T>()
            //Add this object to newly created list:
            addMethod.Invoke(l, new object[] { item.ToType(t) });
        }

        //return List of T objects:
        return l;
    }

}

...在服务器上有一些不太好的分支:

public interface IClient
{
    T Get<T>(Expression<Func<T, bool>> query);
}

public class Person
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class Client
{
    public T Get<T>(Expression<Func<dynamic, bool>> query)
    {
        var serializer = new ExpressionSerializer(new JsonSerializer());
        var serializedExpression = serializer.SerializeText(query);
        return (T)Server.Retrieve(serializedExpression, typeof(T).FullName);
    }
}

public static class Server
{
    public static dynamic Retrieve(string serializedExpression, string targetType)
    {
        var people = new List<dynamic> 
            {
                new { Name = "John", Email = "john@stackoverflow.com" },
                new { Name = "Jane", Email = "jane@stackoverflow.com" }
            };

        // Try creating an object of the type hint passed to the server
        var typeInstance = Activator.CreateInstance(Type.GetType(targetType));

        if (typeInstance.GetType() == typeof(Person))
        {
            var serializer = new ExpressionSerializer(new JsonSerializer());
            var deserializedExpression = (Expression<Func<Person, bool>>)serializer.DeserializeText(serializedExpression);
            var peopleCasted = (IEnumerable<Person>)people.ToNonAnonymousList(typeof(Person));
            return peopleCasted.Where(deserializedExpression.Compile()).SingleOrDefault();
        }
        else
        {
            throw new ArgumentException("Type is unknown");
        }
    }
}

和一个工作测试:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void MyTestMethod()
    {
        var client = new Client();
        var john = client.Get<Person>(p => p.Name == "John");
        Assert.IsNotNull(john);
    }
}

【讨论】:

  • 感谢 Nate 的建议,但除非我遗漏了什么,否则我认为我在问题中可能没有足够强调我的示例中的 Server() 在一个完全独立的进程中运行,而不是客户端 - 通常在另一台机器上。这确实是问题的症结所在,因为我的服务器进程不了解 Person 类型,因此它无法对其进行任何类型的反射,因为应用程序域将无法找到该类型。我尝试了一些解决方案,将类型信息序列化并尝试在服务器上动态创建类型,但也没有运气。
  • 啊,好的。所以在Server() 中绝对没有办法知道Person 的任何事情? (即使您只是复制类定义或其他什么?)
  • 好吧,我不这么认为,但我可能错了——我确实尝试过将类型信息序列化到服务器的路径,然后使用它在服务器上创建类型AssemblyBuilder.DefineDynamicModule() 但不幸的是,我没有成功。同样使用该策略,您必须处理子类型和东西,看起来它会很快变得混乱。
  • ...但更直接地回答您的问题 - 无法在编译时与服务器共享类型信息。我不确定是否可以在运行时通过传入序列化类型信息或其他方式。
  • 明白了。是的,正在查看this 并想知道是否有可能编写一个可以在另一端重建目标类型的自定义反序列化器。我想对象继承将是一个巨大的 PITA。
猜你喜欢
  • 2011-01-07
  • 1970-01-01
  • 2012-06-21
  • 2020-03-20
  • 2023-04-07
  • 2018-11-14
  • 1970-01-01
  • 2011-03-21
相关资源
最近更新 更多