我正在构建一些 blazor 应用程序 并希望有一些可以从共享项目返回任何类型的通用方法。
这个想法是我想做的:
List<GSPparam> parameters.... // stored proc params
List<person> ret = await this._ihttp.Post<List<person>>("api/...",parameters )
在 API 中:
[HttpPost]
[Route("...")]
public async Task<T> ExecProcTable<T>( [FromBody] List<GSPparam> parameters)
{
....
somehow KNOWN WHAT THE T IS ?
in this case List<person>
so
List<person> ret = ..... ;
return ok (ret)
}
但我不知道如何从共享项目传递此类型。 通过字符串?和一些反思? 请指教。
这并不容易,因为 json 没有
T
的概念。
要么使用各种评论中建议的系统之一,要么编写自己的代码。
我的方法是创建一个基本控制器。这是一个 CQS 数据管道,而不是好的旧存储库模式,但它演示了概念:
[ApiController]
public abstract class AppControllerBase<TRecord>
: ControllerBase
where TRecord : class, new()
{
protected ICQSDataBroker _dataBroker;
public AppControllerBase(ICQSDataBroker dataBroker)
=> _dataBroker = dataBroker;
[Mvc.Route("/api/[controller]/listquery")]
[Mvc.HttpPost]
public async Task<ListProviderResult<TRecord>> ListQuery([FromBody] ListQuery<TRecord> query)
=> await _dataBroker.ExecuteAsync<TRecord>(query);
[Mvc.Route("/api/[controller]/addrecordcommand")]
[Mvc.HttpPost]
public async Task<CommandResult> AddRecordCommand([FromBody] AddRecordCommand<TRecord> command)
=> await _dataBroker.ExecuteAsync<TRecord>(command);
/....
}
然后像这样实现真正的控制器:
[ApiController]
public class DboWeatherForecastController : AppControllerBase<DboWeatherForecast>
{
public DboWeatherForecastController(ICQSDataBroker dataBroker)
: base(dataBroker)
{ }
}
调用的 API 数据代理端如下所示:
public async ValueTask<ListProviderResult<TRecord>> ExecuteAsync<TRecord>(ListQuery<TRecord> query) where TRecord : class, new()
{
ListProviderResult<TRecord>? result = null;
var entityname = (new TRecord()).GetType().Name;
var response = await _httpClient.PostAsJsonAsync<ListQuery<TRecord>>($"/api/{entityname}/listquery", query);
if (response.IsSuccessStatusCode)
result = await response.Content.ReadFromJsonAsync<ListProviderResult<TRecord>>();
return result ?? new ListProviderResult<TRecord>();
}
public async ValueTask<CommandResult> ExecuteAsync<TRecord>(AddRecordCommand<TRecord> command) where TRecord : class, new()
{
CommandResult? result = null;
var entityname = (new TRecord()).GetType().Name;
var response = await _httpClient.PostAsJsonAsync<AddRecordCommand<TRecord>>($"/api/{entityname}/addrecordcommand", command);
if (response.IsSuccessStatusCode)
result = await response.Content.ReadFromJsonAsync<CommandResult>();
return result ?? new CommandResult();
}
在客户端,我有一个 API DataBroker,其方法如下所示:
public async ValueTask<ListProviderResult<TRecord>> ExecuteAsync<TRecord>(ListQuery<TRecord> query) where TRecord : class, new()
{
ListProviderResult<TRecord>? result = null;
var entityname = (new TRecord()).GetType().Name;
this.SetHTTPClientSecurityHeader();
var response = await _httpClient.PostAsJsonAsync<ListQuery<TRecord>>($"/api/{entityname}/listquery", query);
if (response.IsSuccessStatusCode)
result = await response.Content.ReadFromJsonAsync<ListProviderResult<TRecord>>();
return result ?? new ListProviderResult<TRecord>();
}
public async ValueTask<CommandResult> ExecuteAsync<TRecord>(AddRecordCommand<TRecord> command) where TRecord : class, new()
{
CommandResult? result = null;
var entityname = (new TRecord()).GetType().Name;
this.SetHTTPClientSecurityHeader();
var response = await _httpClient.PostAsJsonAsync<AddRecordCommand<TRecord>>($"/api/{entityname}/addrecordcommand", command);
if (response.IsSuccessStatusCode)
result = await response.Content.ReadFromJsonAsync<CommandResult>();
return result ?? new CommandResult();
}
protected virtual void SetHTTPClientSecurityHeader()
{
// add your security headers
}
(为了简单起见,我没有包含所有安全性、异常处理和不良响应内容)。
我将仅使用反射来回答我在其他一些 stackoverflow 主题的帮助下得出的解决方案;)
我正在做这样的事
_ihttp.Post<IEnumerable<T>>("api/GSP/ExecProc/List/" + ProcedureName + "/"+ typeof(T).Name+ "/" + CommandTimeout.ToString(), _parameters, CommandTimeout + 5, cts);
因此将此 T 类型作为字符串发送到 url 中
以及 API 控制器中
private static async Task<IEnumerable> AwaitQueryAsyncResult<T>(Task<IEnumerable<T>> task)
=> await task;
[HttpPost]
[Route("ExecProc/List/{SPname}/{TypeName}/{Timeout:int=30}")]
public async Task<IActionResult> ExecProcList(string SPname,string TypeName, int timeout , [FromBody] List<GSPparam> parameters)
{
Type type = type = Type.GetType("XXX.Shared.Classes." + TypeName + ", XXX.Shared")!;
if (type is null) throw new ArgumentException(string.Format("type {0} not found", TypeName));
IEnumerable l = (IEnumerable)Activator.CreateInstance(typeof(List<>).MakeGenericType(type))!;
System.Reflection.MethodInfo method = _sql.GetType().GetMethods().Where(c => c.Name == ("QueryAsync") && c.GetParameters()[1].ParameterType == typeof(List<GSPparam>)).First().MakeGenericMethod(new Type[] { type });
object[] args = { SPname, parameters, CommandType.StoredProcedure, timeout, "Default" };
var task = method.Invoke(this._sql, args);
var miAwaitQueryAsyncResult = typeof(GSPController).GetMethod(nameof(AwaitQueryAsyncResult), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)!
.MakeGenericMethod(type);
var resultAsEnumerable = await (Task<IEnumerable>)miAwaitQueryAsyncResult.Invoke(null, new[] { task })!;
return Ok(resultAsEnumerable);
请让我知道您对这种解决方案有何看法。该类型必须位于共享类命名空间中,仅此而已? 最好的问候