1

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?

Gaute Løken
  • 7,522
  • 3
  • 20
  • 38

2 Answers2

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.

carlosfigueira
  • 85,035
  • 14
  • 131
  • 171
  • I allready have an IDispatchMessageFormatter, and know where I can plug in to do the custom deserialization, but the problem is that I would have search for my non-namespaced typename in all loaded assemblies, which sounds very inefficient to me. Or I would have to plug in a dependency on the assembly I want to load type-information from. But my dispatch formatter is for newtonsoft json serialization and should have no such dependency. So either the inspector would have to trigger before the formatter, or I would have to have two formatters, one for each purpose. Not sure if that's possible. – Gaute Løken Nov 21 '11 at 18:53
  • 1
    The formatter needs to be aware of the types it's deserializing the request into. One possible solution is to have your formatter wrap the newtonsoft one, and hand over to it an operation description with a string in the place of the `Type` parameter (i.e., a fake operation description), then your formatter would first delegate the call to `DeserializeRequest` to it, then look at the parameters whose types were changed, and for those cases, it would use some logic to map the type name into the actual type. – carlosfigueira Nov 22 '11 at 00:00
  • That's actually pretty smart @carlosfigueira. Not as elegant as I would have hoped, but something to consider. Thanks! :) – Gaute Løken Nov 22 '11 at 08:54
  • I'm now looking into the WCF Web API project. With parameter providers, are you refering to HttpOperationHandlers, or some other type? Asked for some pointers for documentation here: http://stackoverflow.com/questions/8244747/where-can-i-find-up-to-date-documentation-about-the-wcf-web-api – Gaute Løken Nov 23 '11 at 15:33
  • Yep, swapping to the WCF Web API project worked wonders for me. Loving the test client as well. Thanks for pointing me in the right direction @carlosfigueira. – Gaute Løken Nov 24 '11 at 16:00
0

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.

Rajesh
  • 7,766
  • 5
  • 22
  • 35
  • Thanks, but I'm afraid that doesn't help me much. I'm aware of this sort of approach. I would have to call Assembly.GetAssembly(typeof(MyClass1)) rather than executing assembly. This would be more refactorable than my magic-string example. But it still would not work for any Type in any assembly. As it requires either iterating all loaded assemblies or knowing the namespace. – Gaute Løken Nov 21 '11 at 18:49