运行时创建C#Web API 2控制器时出现异常
我想在启动时动态生成WebAPI2控制器。在本例中,我使用了反射 然而,当我编译它并启动服务时,当我通过HttpRequest调用它时出现异常。例外情况如下所示:运行时创建C#Web API 2控制器时出现异常,c#,reflection,controller,asp.net-web-api2,reflection.emit,C#,Reflection,Controller,Asp.net Web Api2,Reflection.emit,我想在启动时动态生成WebAPI2控制器。在本例中,我使用了反射 然而,当我编译它并启动服务时,当我通过HttpRequest调用它时出现异常。例外情况如下所示: { "Message": "An error has occurred.", "ExceptionMessage": "Unable to cast object of type 'System.Object[]' to type 'System.Web.Http.ParameterBindingAttribute[]
{
"Message": "An error has occurred.",
"ExceptionMessage": "Unable to cast object of type 'System.Object[]' to type 'System.Web.Http.ParameterBindingAttribute[]'.",
"ExceptionType": "System.InvalidCastException",
"StackTrace": " at System.Web.Http.Controllers.ReflectedHttpParameterDescriptor.GetCustomAttributes[TAttribute]() at System.Web.Http.Controllers.HttpParameterDescriptor.FindParameterBindingAttribute() at System.Web.Http.Controllers.HttpParameterDescriptor.get_ParameterBinderAttribute() at System.Web.Http.ModelBinding.DefaultActionValueBinder.GetParameterBinding(HttpParameterDescriptor parameter) at System.Array.ConvertAll[TInput,TOutput](TInput[] array, Converter`2 converter) at System.Web.Http.ModelBinding.DefaultActionValueBinder.GetBinding(HttpActionDescriptor actionDescriptor) at System.Web.Http.Controllers.HttpActionDescriptor.get_ActionBinding() at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem..ctor(HttpControllerDescriptor controllerDescriptor) at System.Web.Http.Controllers.ApiControllerActionSelector.GetInternalSelector(HttpControllerDescriptor controllerDescriptor) at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext) at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
//ControllerBuilder.cs
//用于实现自定义控制器的生成器。问题似乎就在这里。也许我在这一部分配置错了?
公共静态类控制器生成器
{
公共静态ApicController CreateNewObject()
{
var ctrlType=CompileResultType(“TmpController”,新字典{{“tmpField”,“System.String”});
var ctrlObject=Activator.CreateInstance(ctrlType);
将ctrlObject返回为ApiController;
}
公共静态类型编译器结果类型(字符串类型签名,字典属性)
{
TypeBuilder tb=GetTypeBuilder(typeSignature);
tb.SetParent(typeof(ApiController));
ConstructorBuilder ctor=tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
foreach(propDic中的var项目)
{
CreateProperty(tb,item.Key,Type.GetType(item.Value));
}
//对于这个控制器,我只需要一个Get方法来处理Get请求
MethodBuilder myGetMethod=
tb.DefineMethod(“获取”,
方法属性。公共,
typeof(String),新类型[]{typeof(String)};
//为方法生成IL。
ILGenerator myMethodIL=myGetMethod.GetILGenerator();
myMethodIL.Emit(操作码.Ldarg_1);
myMethodIL.Emit(操作码.Ret);
类型objectType=tb.CreateType();
返回objectType;
}
私有静态类型生成器GetTypeBuilder(字符串类型签名)
{
var an=新程序集名称(类型签名);
AssemblyBuilder AssemblyBuilder=AppDomain.CurrentDomain.DefinedDynamicAssembly(an,AssemblyBuilderAccess.Run);
ModuleBuilder ModuleBuilder=assemblyBuilder.DefinedDynamicModule(“主模块”);
TypeBuilder tb=moduleBuilder.DefineType(类型签名,
TypeAttributes.Public|
TypeAttributes.Class|
TypeAttributes.AutoClass|
TypeAttributes.AnsiClass|
TypeAttributes.BeforeFieldInit|
TypeAttributes.AutoLayout,
无效);
返回结核病;
}
私有静态void CreateProperty(TypeBuilder tb、字符串propertyName、类型propertyType)
{
FieldBuilder FieldBuilder=tb.DefineField(“\u”+propertyName,propertyType,FieldAttributes.Private);
PropertyBuilder PropertyBuilder=tb.DefineProperty(propertyName,PropertyAttributes.HasDefault,propertyType,null);
MethodBuilder getPropMthdBldr=tb.DefineMethod(“get|”+propertyName,MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,propertyType,Type.EmptyTypes);
ILGenerator getIl=getPropMthdBldr.GetILGenerator();
getIl.Emit(操作码.Ldarg_0);
getIl.Emit(操作码.Ldfld,fieldBuilder);
getIl.Emit(操作码.Ret);
MethodBuilder setPropMthdBldr=
tb.DefineMethod(“设置”+属性名称,
MethodAttributes.Public|
MethodAttributes.SpecialName|
MethodAttributes.hidebysing,
空,新[]{propertyType});
ILGenerator setIl=setPropMthdBldr.GetILGenerator();
Label modifyProperty=setIl.DefineLabel();
Label exitSet=setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(操作码.Ldarg_0);
setIl.Emit(操作码Ldarg_1);
setIl.Emit(操作码.Stfld,fieldBuilder);
setIl.Emit(操作码Nop);
设置标记标签(exitSet);
setIl.Emit(操作码Ret);
SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
我试图通过
ctrlType.GetMethod(“Get”).Invoke(ctrlObject,new[]{“param1”})调用CreateNewObject()中ctrlObject的“Get”方法。但是,我可以得到返回字符串“param1”!!!。那么,我可以知道是什么问题导致了我作为真正的Web API控制器请求时在顶部引用的异常吗?我遇到了同样的问题,花了几个小时才弄清楚到底发生了什么
基本上,ReflectedHttpParameterDescriptor.GetCustomAttributes[TaAttribute]()
试图从您正在创建的get(string)
方法中获取参数信息,如参数名称和属性。问题在于DefineMethod()
只声明参数类型,而不是GetCustomAttributes()
需要的其他信息
解决方案是在定义方法后使用MethodBuilder.DefineParameter()
提供此信息
//对于此控制器,我只需要一个Get方法来处理Get请求
MethodBuilder myGetMethod=tb.DefineMethod(
名称:“获取”,
属性:MethodAttributes.Public,
returnType:typeof(字符串),
parameterTypes:新类型[]{typeof(String)}
);
//定义参数
myGetMethod.DefineParameter(
位置:1,//0为返回值,1为第一个参数,2为第二个参数,依此类推。
属性:参数属性。无,
strParamName:“stringParam”
);
这已经花了我一天时间了。有人能帮我吗?提前谢谢。
// WebApiConfig.cs
// Register function in WebApiConfig
public static void Register(HttpConfiguration config)
{
// Replace services by this line, to add my custom HttpControllerSelector
config.Services.Replace(typeof(IHttpControllerSelector), new CustomHttpControllerSelector(config));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
// CustomeHttpControllerSelector.cs
// CustomHttpControllerSelector class to override the default one (In this case, the same controller will be returned for all request)
public class CustomHttpControllerSelector : DefaultHttpControllerSelector
{
private readonly HttpConfiguration _configuration;
public CustomHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
{
_configuration = configuration;
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var ctrl = ControllerBuilder.CreateNewObject();
String controllerName = base.GetControllerName(request);
return new HttpControllerDescriptor(_configuration, controllerName, ctrl.GetType());
}
}
// ControllerBuilder.cs
// The builder to implement my custom controller. Seems the problem is here. Maybe I configured something wrong in this section?
public static class ControllerBuilder
{
public static ApiController CreateNewObject()
{
var ctrlType = CompileResultType("TmpController", new Dictionary<string, string> { { "tmpField", "System.String" } });
var ctrlObject = Activator.CreateInstance(ctrlType);
return ctrlObject as ApiController;
}
public static Type CompileResultType(string typeSignature, Dictionary<string, string> propDic)
{
TypeBuilder tb = GetTypeBuilder(typeSignature);
tb.SetParent(typeof(ApiController));
ConstructorBuilder ctor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
foreach (var item in propDic)
{
CreateProperty(tb, item.Key, Type.GetType(item.Value));
}
// For this controller, I only want a Get method to server Get request
MethodBuilder myGetMethod =
tb.DefineMethod("Get",
MethodAttributes.Public,
typeof(String), new Type[] { typeof(String) });
// Generate IL for method.
ILGenerator myMethodIL = myGetMethod.GetILGenerator();
myMethodIL.Emit(OpCodes.Ldarg_1);
myMethodIL.Emit(OpCodes.Ret);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder(string typeSignature)
{
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}