嘿我尝试将我的对象转换为该对象应该实现的接口。但在执行代码期间,我遇到以下错误:
无法将GetTreeHandler类型的对象强制转换为IQueryHandler,IQuery< Tree&gt ;.
为什么呢?一切似乎都没问题。
没有使用动态的任何想法?
QueryDispatcher
def demean_and_log(x):
x_log = np.log(x)
x_log_mean = x_log.mean()
return x_log - x_log_mean
log_demean_col = X.groupby(['A'])['B'].transform(demean_and_log)
GetTreeHandler
public async Task<TResult> ExecuteAsync<TResult>(IQuery<TResult> query)
{
if (query == null)
{
throw new ArgumentNullException("Query can not be null.");
}
var handlerType = typeof (IQueryHandler<,>).MakeGenericType(query.GetType(), typeof (TResult));
var handler = _context.Resolve(handlerType);
IQueryHandler<IQuery<TResult>, TResult> castedHandler = (IQueryHandler<IQuery<TResult>, TResult>) handler;
return (TResult) await castedHandler.ExecuteAsync(query);
}
修改
我目前的解决方案(部分有效):
public class GetTreeHandler : IQueryHandler<GetTree, Tree>
{
private readonly ProductContext _context;
public string Name { get; set; }
public GetTreeHandler(ProductContext context)
{
_context = context;
}
public async Task<Tree> ExecuteAsync(GetTree query)
=> await Task.FromResult(
_context.Trees.FirstOrDefault(x => x.Id == query.Id)
);
}
为何部分? 例如。处理程序:
不起作用:
public async Task<TResult> ExecuteAsync<TResult>(IQuery query) where TResult : class
{
if (query == null)
{
throw new ArgumentNullException("Query can not be null.");
}
var handlerType = typeof (IQueryHandler<,>).MakeGenericType(query.GetType(), typeof (TResult));
var handler = _context.Resolve(handlerType);
return await (Task<TResult>)
handler.GetType()
.GetMethod("ExecuteAsync")
.Invoke(handler, new object[]{query});
}
使用:
public class GetIndexesHandler : IQueryHandler<GetIndexes, IEnumerable<AssocIndexDto>>
正如您所看到的,这两个类实现了相同的接口,但具有不同的第二个参数类型。
问题是Resolve方法无法找到已注册的服务,但如果我尝试调试此代码,我可以看到所需服务已正确注册,但无法找到。
你知道如何解决它吗?
答案 0 :(得分:3)
您正在尝试将IQueryHandler<GetTree, Tree>
投射到IQueryHandler<IQuery<Tree>, Tree>
,但只有在IQueryHandler<TQuery, TResult>
指定为:{/ p>}时才会有效
interface IQueryHandler<out TQuery, TResult>
请注意out
参数。
out
关键字将TQuery
标记为输出参数。如果将泛型类型参数标记为输出参数,则允许我们将接口强制转换为更通用的参数。例如,IEnumerable<string>
可以转换为IEnumerable<object>
,因为IEnumerable<string>
将返回字符串,并且它们可以表示为对象。
然而,对于in
个参数,则相反。在查看Action<in T>
for intance时,我们可以将Action<BaseType>
转换为Action<SubType>
,因为该操作始终也可以处理SubType
。另一方面,将Action<BaseType>
转换为Action<object>
是不可能的,因为这样我们也可以将string
传递给动作(因为string
是一个object
),但那显然会在运行时失败。
只有在IQueryHandler<GetTree, Tree>
标有IQueryHandler<IQuery<Tree>, Tree>
关键字的情况下才能将TQuery
转换为out
,但显然TQuery
是输入参数。因此,CLR禁止转换,因为它可能在运行时导致错误。
但是,在你的情况下,这个问题不存在,因为你知道传入的类型总是适合。但请记住,CLR无法检查这一点,因此阻止了转换。
我的博客上的一个解决方案是described,使用dynamic
关键字,如下所示:
public async Task<TResult> ExecuteAsync<TResult>(IQuery<TResult> query)
{
var handlerType =
typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
dynamic handler = _context.Resolve(handlerType);
return (TResult)await castedHandler.ExecuteAsync((dynamic)query);
}
另一个选项是指定并注册实现缺少TQuery
泛型类型的接口的通用包装类型。这样您就可以完全阻止使用dynamic
:
public interface IWrapper<TResult>
{
Task<TResult> ExecuteAsync(IQuery<TResult> query);
}
public async Task<TResult> ExecuteAsync<TResult>(IQuery<TResult> query)
{
var wrapperType =
typeof(Wrapper<,>).MakeGenericType(query.GetType(), typeof(TResult));
var wrapper = (IWrapper<TResult>)_context.Resolve(wrapperType);
return wrapper.ExecuteAsync(query);
}
// Don't forget to register this type by itself in Autofac.
public class Wrapper<TQuery, TResult> : IWrapper<TResult>
{
private readonly IQueryHandler<TQuery, TResult> handler;
public Wrapper(IQueryHandler<TQuery, TResult> handler) { this.handler = handler; }
Task<TResult> IWrapper<TResult>.ExecuteAsync(IQuery<TResult> query) =>
this.handler.ExecuteAsync((TQuery)query);
}