无法将解析的对象强制转换为通用抽象 autofac C#



嘿,我尝试将我的对象转换为该对象应该实现的接口。但是在执行代码期间,我遇到以下错误:

无法将 GetTreeHandler 类型的对象强制转换为 IQueryHandler、IQuery

为什么?一切似乎都很好。

不使用动态的任何想法?

查询调度程序

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>>

工程:

public class GetIndexesHandler : IQueryHandler<GetIndexes,AssocIndexDto>

如您所见,这两个类实现相同的接口,但具有不同的第二个参数类型。

问题是 Resolve 方法找不到已注册的服务,但是如果我尝试调试此代码,我可以看到想要的服务已正确注册,但找不到。

你知道怎么解决吗?

您正在尝试将IQueryHandler<GetTree, Tree>转换为IQueryHandler<IQuery<Tree>, Tree>,但这仅在IQueryHandler<TQuery, TResult>被指定为:

interface IQueryHandler<out TQuery, TResult>

请注意此处的out参数。

out关键字将TQuery标记为输出参数。如果泛型类型参数被标记为输出参数,它允许我们将接口转换为更泛型的参数。例如,可以将IEnumerable<string>转换为IEnumerable<object>,因为IEnumerable<string>将返回字符串,并且可以将它们表示为对象。

然而,对于in论点,情况正好相反。在查看意图Action<in T>时,我们可以向Action<SubType>Action<BaseType>,因为该动作也始终能够处理SubType。另一方面,不可能将Action<BaseType>转换为Action<object>,因为这将允许我们也将string传递给操作(因为string是一个object),但这显然会在运行时失败。

因此,只有当TQuery标有out关键字时,才有可能将IQueryHandler<GetTree, Tree>转换为IQueryHandler<IQuery<Tree>, Tree>,但显然TQuery是输入参数。因此,CLR 禁止转换,因为它可能导致运行时出错。

但是,在您的情况下,此问题将不存在,因为您知道传入的类型始终适合。但请记住,CLR 无法检查这一点,因此会阻止转换。

正如我在博客上描述的那样,一种解决方案是使用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);
}

最新更新