Deserialize a string from the uri template to an o

2019-08-02 03:40发布

问题:

I have this restfull service that I want to be able to call for all classes in a certain namespace or assembly. And I'm going to need to know for which Type it is called inside the operation. In short I want a template that allows both of these:

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

I want my uri templates to look something like this:

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

And my operation will be something like this:

public void ClassSpecificOfSomeFurtherSpec(Type type, string info)

I'm assuming there's no way to do anything like the following, which I guess is what I'm really trying to achieve. After all, generics have to be decided at compile time I guess. But it illuminates the problem:

public void ClassSpecificOfSomeFurtherSpec<type>(string info)

The problem here is that the input for class is a the short Name of a Type, rather than the FullName, which means Type.GetType(class, true, true) won't work. If it would I could use that in my custom deserialization, and everything would be fine. But since it won't, I'm reluctant to hardcode my namespace into my generic deserialization, since it would make it utterly non-generic.

If I were to add an IParameterInspector, could I then modify the parameter (or only inspect it), and tack on the namespace before the deserialization takes place, or will the order be the other way around?

I've got this feeling that I'm overcomplicating things. Is there a no-brainer way to do this sort of thing that I've missed, or some really convincing reason why I shouldn't generalize my services this much?

Bottom line is that I want to avoid this sort of code for every single operation:

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

Update1

So, I found this nice blog post by Carlos describing how to add the option of turning an IParameterInspector into an improved version that can also modify parameters. In the blog he only modifies return values. What I wanted to do was modify the input value. So, I added that capability and added my modifier class:

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;
    }
}

But, of course they've added some blocks to prevent you from doing this sort of thing:

[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

Which points me in a completely new direction. Is there any way to override the UriTemplateClientFormatter or related classes to achieve what I want?

回答1:

Possible it is, but you'll have to write your own IDispatchMessageFormatter which is both aware of UriTemplates and knows how to convert between string and types - not an easy task. This gets easier with the WCF Web API project (see http://wcf.codeplex.com), where you have more flexibility over the parameter providers.



回答2:

One way you can achieve it is to load all the types into memory and then try to find the type whose name matches the string and create that type. But also you need to make sure that the same type doesnt exists in different namespaces with same name.

Some sample code which i have written to lookup the types in a executing assembly is below:

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----");
            }

This is not an exact solution but just providing for you to load a type in the executing assembly.

Hope it helps you to some extent.



标签: .net wcf rest