简体   繁体   English

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

[英]Unable to cast resolved object to generic abstraction autofac C#

Hey I try to cast my object to interface which this object should implements. 嘿我尝试将我的对象转换为该对象应该实现的接口。 But during executing code I have following error: 但在执行代码时我遇到以下错误:

Unable to cast object of type GetTreeHandler to IQueryHandler, IQuery<Tree>. 无法将GetTreeHandler类型的对象强制转换为IQueryHandler,IQuery <Tree>。

Why? 为什么? Everything seems to be ok. 一切似乎都没问题。

Any idea without using dynamic? 没有使用动态的任何想法?

QueryDispatcher QueryDispatcher

    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);    
    }   

GetTreeHandler GetTreeHandler

   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)
                );
    }

EDIT 编辑

My current solution(partially works): 我目前的解决方案(部分工作):

    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});   
    }  

Why partially? 为何部分? Eg. 例如。 handlers: 处理:

Doesn't work: 不起作用:

public class GetIndexesHandler : IQueryHandler<GetIndexes, IEnumerable<AssocIndexDto>>

Works: 作品:

public class GetIndexesHandler : IQueryHandler<GetIndexes,AssocIndexDto>

As you can see these two classes implements the same interface but with different second parameter type. 如您所见,这两个类实现了相同的接口,但具有不同的第二个参数类型。

The problem is that Resolve method cannot find the registered service, but if I try to debug this code, I can see that the wanted service is registed correctly, but it can't be found. 问题是Resolve方法无法找到已注册的服务,但如果我尝试调试此代码,我可以看到所需服务已正确注册,但无法找到。

Have you any idea how to solve it? 你知道如何解决它吗?

You are trying to cast an IQueryHandler<GetTree, Tree> to an IQueryHandler<IQuery<Tree>, Tree> , but that would only work when IQueryHandler<TQuery, TResult> was specified as: 您正在尝试将IQueryHandler<GetTree, Tree>转换为IQueryHandler<IQuery<Tree>, Tree> ,但这仅在IQueryHandler<TQuery, TResult>指定为:

interface IQueryHandler<out TQuery, TResult>

Notice the out argument here. 注意这里的out参数。

The out keyword marks TQuery as an output argument. out关键字将TQuery标记为输出参数。 In case a generic type argument is marked as output argument, it allows us to cast the interface to something more generic. 如果将泛型类型参数标记为输出参数,则允许我们将接口强制转换为更通用的参数。 An IEnumerable<string> for instance can be casted to IEnumerable<object> since IEnumerable<string> will return strings, and they can be represented as objects. 例如, IEnumerable<string>可以转换为IEnumerable<object>因为IEnumerable<string>将返回字符串,并且它们可以表示为对象。

For in arguments however, the opposite holds. 然而, in论证中,情况正好相反。 When looking at an Action<in T> for intance, we are allowed to cast an Action<BaseType> to Action<SubType> , since the action would always be able to process SubType s as well. 当查看Action<in T> for intance时,我们可以将Action<BaseType>Action<SubType> ,因为该操作总是能够处理SubType On the other hand, it would be impossible to cast Action<BaseType> to Action<object> since that would allow us to pass in a string to the action as well (since string is an object ), but that would then obviously fail at runtime. 另一方面,将Action<BaseType>Action<object>是不可能的,因为这样我们也可以将string传递给动作(因为string是一个object ),但是这显然会在运行。

So casting an IQueryHandler<GetTree, Tree> to IQueryHandler<IQuery<Tree>, Tree> would only be possible if TQuery is marked with an out keyword, but clearly TQuery is an input argument. 因此IQueryHandler<IQuery<Tree>, Tree>只有在TQuery标记为out关键字时才能将IQueryHandler<GetTree, Tree>IQueryHandler<IQuery<Tree>, Tree> ,但显然TQuery是输入参数。 The CLR therefore forbids the conversion, since it could lead to errors at runtime. 因此,CLR禁止转换,因为它可能在运行时导致错误。

In your case however, this problem would not exist, because you know that the passed in type always fits. 但是,在你的情况下,这个问题不存在,因为你知道传入的类型总是适合。 But remember, the CLR is unable to check this, and is therefore blocking the conversion. 但请记住,CLR无法检查这一点,因此阻止了转换。

One solution is, as I described on my blog, to use the dynamic keyword as follows: 正如我在博客中所描述的 ,一种解决方案是使用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);    
}

Another option is to specify and register a generic wrapper type that implements an interface that lacks the TQuery generic type. 另一种选择是指定和注册实现缺少TQuery泛型类型的接口的通用包装器类型。 This way you can prevent using dynamic altogether: 这样您就可以完全阻止使用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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM