将字符串从uri模板反序列化为操作Type参数

时间:2011-11-21 16:23:15

标签: .net wcf rest

我有这个restfull服务,我希望能够调用某个命名空间或程序集中的所有类。而且我将需要知道它在操作中被调用的类型。简而言之,我想要一个允许这两者的模板:

../MyClass1/some/further/spec
../MyClass2/some/further/spec

我希望我的uri模板看起来像这样:

/{type}/some/further/{info}

我的操作将是这样的:

public void ClassSpecificOfSomeFurtherSpec(Type type, string info)

我假设没有办法做以下事情,我想这就是我真正想要实现的目标。毕竟,我猜想必须在编译时确定泛型。但它解决了这个问题:

public void ClassSpecificOfSomeFurtherSpec<type>(string info)

这里的问题是类的输入是Type的短名称,而不是FullName,这意味着Type.GetType(class,true,true)将不起作用。如果我可以在我的自定义反序列化中使用它,一切都会好的。但由于它不会,我不愿意将我的命名空间硬编码到我的通用反序列化中,因为它会使它完全不通用。

如果我要添加一个IParameterInspector,我可以修改参数(或只检查它),并在反序列化发生之前添加命名空间,或者顺序是否相反?

我有这种感觉,我太复杂了。是否有一种简单的方法来做我错过的这类事情,或者一些非常令人信服的理由为什么我不应该这么概括我的服务呢?

底线是我想避免每一个操作的这种代码:

public Response ClassSpecificOfSomeFurtherSpec(string type, string spec)
{
    Type theType = Type.GetType("My.NameSpaced."+type, true, true);
    //.. and the stuff that does things
}

UPDATE1

因此,我发现Carlos的这个很好blog post描述了如何添加将IParameterInspector转换为可以修改参数的改进版本的选项。在博客中,他只修改了返回值。我想要做的是修改输入值。所以,我添加了这个功能并添加了我的修饰符类:

public class EntityParameterModifier : IParameterModifier
{
    public OperationDescription Description { get; private set; }

    public EntityParameterModifier(OperationDescription description)
    {
        Description = description;
    }

    public object BeforeCall(string operationName, ref object[] inputs)
    {
        var parts = Description.Messages[0].Body.Parts;
        for (int i = 0; i < parts.Count; i++)
        {
            if (parts[i].Type == typeof(Type) && inputs[i].GetType() == typeof(string))
                inputs[i] = EntityStringToType((string)inputs[i]);
        }

        return null;
    }

    public void AfterCall(string operationName, object[] outputs, ref object returnValue, object correlationState) { }

    private Type EntityStringToType(string entityString)
    {
        Assembly assembly = Assembly.GetAssembly(typeof(Entity));
        Type type = (from t in assembly.GetTypes()
                     where t.Name == entityString || t.FullName == entityString
                     select t).SingleOrDefault<Type>();
        return type;
    }
}

但是,他们当然添加了一些块来阻止你做这类事情:

[InvalidOperationException: Operation 'ClassSpecificOfSomeFurtherSpec' in contract 
'IMyServiceContract' has a path variable named 'type' which does not have type 'string'.
Variables for UriTemplate path segments must have type 'string'.]
System.ServiceModel.Dispatcher.UriTemplateClientFormatter.Populate(Dictionary`2& pathMapping, Dictionary`2& queryMapping, Int32& totalNumUTVars, UriTemplate& uriTemplate, OperationDescription operationDescription, QueryStringConverter qsc, String contractName) +1128

这使我指向一个全新的方向。有没有办法覆盖UriTemplateClientFormatter或相关的类来实现我想要的?

2 个答案:

答案 0 :(得分:1)

可能是这样,但你必须编写自己的IDispatchMessageFormatter,它既知道UriTemplates又知道如何在字符串和类型之间进行转换 - 这不是一件容易的事。使用WCF Web API项目(请参阅http://wcf.codeplex.com)可以更轻松地获得参数提供程序的灵活性。

答案 1 :(得分:0)

您可以实现的一种方法是将所有类型加载到内存中,然后尝试查找名称与字符串匹配的类型并创建该类型。但是,您还需要确保在具有相同名称的不同命名空间中不存在相同类型。

我编写的一些示例代码用于查找正在执行的程序集中的类型:

Assembly ass = Assembly.GetExecutingAssembly();
            foreach (Type t in ass.GetTypes())
            {
                Console.WriteLine(t.Name);
                Console.WriteLine(t.Namespace);
                Console.WriteLine(t.FullName);
                Console.WriteLine("----End of type----");
            }

这不是一个确切的解决方案,只是为您提供在执行程序集中加载类型。

希望它在某种程度上对你有帮助。