使用task.run()时,C#autofac参数filterattribute悬挂

发布于 2025-01-27 10:13:58 字数 5088 浏览 3 评论 0原文

我有3个类:Apple1Apple2Apple3Apple2取决于Apple1Apple3取决于Apple2,如下所示。

public class Apple1
{
}
public class Apple2
{
    public Apple2(Apple1 apple1) {}
}
public class Apple3
{
    public Apple3([Optional] Apple2 apple2) {}
}

我将三个注册到autoFac中

var builder = new ContainerBuilder();
builder.RegisterType<Apple1>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
builder.RegisterType<Apple2>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
builder.RegisterType<Apple3>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();

[可选]属性是我的自定义属性,该属性从parameterfilterattribute

[AttributeUsage(AttributeTargets.Parameter)]
public class OptionalAttribute : ParameterFilterAttribute
{
    public override object? ResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        try
        {
            if (context.TryResolve(parameter.ParameterType, out object? instance))
            {
                return instance;
            }
            return null;
        }
        catch (DependencyResolutionException)
        {
            return null;
        }
    }
    public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        return true;
    }
}

当我解析apple 3时,作为constructor参数Apple2 Apple2[可选]装饰,此optionalAttribute snippet Works and sosolves参数。它可以作为我的期望工作:如果成功解决或返回null,则返回实例,如果失败/故障。 但是,一旦resolve参数中的代码用task.run()包装,它将始终挂起。

[AttributeUsage(AttributeTargets.Parameter)]
public class OptionalAttribute : ParameterFilterAttribute
{
    public override object? ResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        var task = Task.Run(() => 
        {
            try
            {
                Console.WriteLine("Start task");
                // The below statement never finishes.
                if (context.TryResolve(parameter.ParameterType, out object? instance))
                {
                    Console.WriteLine("TryResolve: true");
                    return instance;
                }
                Console.WriteLine("TryResolve: false");
                return null;
            }
            catch (DependencyResolutionException)
            {
                Console.WriteLine("Exception");
                return null;
            }
        });
        return task.Result;
    }
    public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        return true;
    }
}

输出将仅是

Start Task

整个源代码,

using Autofac;
using Autofac.Core;
using Autofac.Features.AttributeFilters;
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace Autofac.Issue
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Apple1>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
            builder.RegisterType<Apple2>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
            builder.RegisterType<Apple3>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
            
            var container = builder.Build();
            container.Resolve<Apple3>();
        }
    }
    public class Apple1
    {
    }
    public class Apple2
    {
        public Apple2(Apple1 apple1)
        {
        }
    }
    public class Apple3
    {
        public Apple3([Optional] Apple2 apple2)
        {
        }
    }
    [AttributeUsage(AttributeTargets.Parameter)]
    public class OptionalAttribute : ParameterFilterAttribute
    {
        public override object? ResolveParameter(ParameterInfo parameter, IComponentContext context)
        {
            var task = Task.Run(() =>
            {
                try
                {
                    Console.WriteLine("Start task");
                    if (context.TryResolve(parameter.ParameterType, out object? instance))
                    {
                        Console.WriteLine("TryResolve: true");
                        return instance;
                    }
                    Console.WriteLine("TryResolve: false");
                    return null;
                }
                catch (DependencyResolutionException)
                {
                    Console.WriteLine("Exception");
                    return null;
                }
            });
    
            return task.Result;
        }
        public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
        {
            return true;
        }
    }
}

就像context.tryresoletask.run()有冲突。有人可以看看吗?

I have 3 classes: Apple1, Apple2, and Apple3. Apple2 depends on Apple1 and Apple3 depends on Apple2 like follows.

public class Apple1
{
}
public class Apple2
{
    public Apple2(Apple1 apple1) {}
}
public class Apple3
{
    public Apple3([Optional] Apple2 apple2) {}
}

I register the three into Autofac

var builder = new ContainerBuilder();
builder.RegisterType<Apple1>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
builder.RegisterType<Apple2>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
builder.RegisterType<Apple3>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();

The [Optional] attribute is my customized attribute inherited from ParameterFilterAttribute like the following

[AttributeUsage(AttributeTargets.Parameter)]
public class OptionalAttribute : ParameterFilterAttribute
{
    public override object? ResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        try
        {
            if (context.TryResolve(parameter.ParameterType, out object? instance))
            {
                return instance;
            }
            return null;
        }
        catch (DependencyResolutionException)
        {
            return null;
        }
    }
    public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        return true;
    }
}

When I resolve Apple3, as the constructor parameter Apple2 apple2 is decorated by [Optional], this OptionalAttribute snippet works and resolves parameters. It can work as my expectation: return the instance if successful to resolve or return null if failing/faulted.
But once the code in ResolveParameter it wrappered with Task.Run(), it will always hang.

[AttributeUsage(AttributeTargets.Parameter)]
public class OptionalAttribute : ParameterFilterAttribute
{
    public override object? ResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        var task = Task.Run(() => 
        {
            try
            {
                Console.WriteLine("Start task");
                // The below statement never finishes.
                if (context.TryResolve(parameter.ParameterType, out object? instance))
                {
                    Console.WriteLine("TryResolve: true");
                    return instance;
                }
                Console.WriteLine("TryResolve: false");
                return null;
            }
            catch (DependencyResolutionException)
            {
                Console.WriteLine("Exception");
                return null;
            }
        });
        return task.Result;
    }
    public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
    {
        return true;
    }
}

The output will be only

Start Task

The entire source code is

using Autofac;
using Autofac.Core;
using Autofac.Features.AttributeFilters;
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace Autofac.Issue
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Apple1>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
            builder.RegisterType<Apple2>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
            builder.RegisterType<Apple3>().AsSelf().InstancePerLifetimeScope().WithAttributeFiltering();
            
            var container = builder.Build();
            container.Resolve<Apple3>();
        }
    }
    public class Apple1
    {
    }
    public class Apple2
    {
        public Apple2(Apple1 apple1)
        {
        }
    }
    public class Apple3
    {
        public Apple3([Optional] Apple2 apple2)
        {
        }
    }
    [AttributeUsage(AttributeTargets.Parameter)]
    public class OptionalAttribute : ParameterFilterAttribute
    {
        public override object? ResolveParameter(ParameterInfo parameter, IComponentContext context)
        {
            var task = Task.Run(() =>
            {
                try
                {
                    Console.WriteLine("Start task");
                    if (context.TryResolve(parameter.ParameterType, out object? instance))
                    {
                        Console.WriteLine("TryResolve: true");
                        return instance;
                    }
                    Console.WriteLine("TryResolve: false");
                    return null;
                }
                catch (DependencyResolutionException)
                {
                    Console.WriteLine("Exception");
                    return null;
                }
            });
    
            return task.Result;
        }
        public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
        {
            return true;
        }
    }
}

It's like context.TryResole has conflicts with Task.Run(). Could anyone take a look?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦回旧景 2025-02-03 10:13:58

我没有讨论过这个(老实说,没有时间),但是您可能会陷入僵局,因为事物已注册instanceperlifetimescope。为了确保在每生范围内创建给定的组件一次和唯一的范围,首次锁定了类似的内容。而且,当然,如果那东西具有自己的依赖关系,那也是instanceperlifetimesscope,那将导致锁定链条。然后,您将异步操作投入Resolve 的中间,这将是个坏消息。

解决一个对象不是异步操作。您可以从同一寿命范围上解决两个不同的线程上的两个不同的东西,但是您无法解决一个线程上的一部分,在另一个线程上的一部分在另一个线程上。

想想容器更像是构造函数。构造函数不是异步,对吗?解决操作都不应该是异步。

这里的答案是从属性中删除task.run。您实际上不会从中得到任何东西,而且实际上您实际上会使性能更糟糕的,因为您没有在单线线程上进行快速操作,而是添加了产卵的开销一个新线程,以后重新加入。

如果您有某种创建对象异步的工厂,则需要将工厂注入构造函数,并将其调用async工厂方法自己在构造函数中。试图将同步物中的同步和异步混合在完全同步的对象构造中是不良时代的食谱。

I haven't debugged into this (and, honestly, don't have time) but you're likely getting deadlocks because things are registered InstancePerLifetimeScope. To ensure a given component is created once-and-only-once per lifetime scope, there is locking the first time something like that is created. And, of course, if that thing has its own dependencies that are also InstancePerLifetimeScope, that's going to cause locking down the chain. You're then throwing async operations into the middle of a resolve and that's going to be bad news.

Resolving an object is not an asynchronous operation. You can resolve two different things on two different threads from the same lifetime scope, sure, but you can't resolve part of a thing on one thread and part of the same thing on a different thread.

Think of a container resolve more like a constructor. Constructors are not asynchronous, right? Neither should resolve operations be async.

The answer here is to remove the Task.Run from the attribute. You won't actually get anything from it, and likely you're actually making the performance worse because instead of doing the fast operation of resolving something on the single thread, you're adding the overhead of spawning a new thread and re-joining later.

If you have some sort of factory that creates objects async, then you need to inject the factory into your constructor and call the async factory method yourself in your constructor. Trying to mix the sync and async in a fully synchronous object construction is a recipe for bad times.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文