将 uri 模板中的字符串反序列化为操作类型参数
我有这个安静的服务,我希望能够为某个命名空间或程序集中的所有类调用它。我需要知道在操作中调用哪个类型。简而言之,我想要一个允许这两者的模板:
../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.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,描述如何添加将 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 或相关类来实现我想要的?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
可能是这样,但您必须编写自己的 IDispatchMessageFormatter,它既了解 UriTemplates,又知道如何在字符串和类型之间进行转换 - 这不是一件容易的任务。使用 WCF Web API 项目(请参阅 http://wcf.codeplex.com),这会变得更容易,您可以在其中比参数提供者更灵活。
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.实现它的一种方法是将所有类型加载到内存中,然后尝试查找名称与字符串匹配的类型并创建该类型。但您还需要确保相同的类型不存在于具有相同名称的不同命名空间中。
我编写的用于查找执行程序集中的类型的一些示例代码如下:
这不是一个精确的解决方案,只是为您提供在执行程序集中加载类型的方法。
希望它能在一定程度上帮助你。
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:
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.