使用task.run()时,C#autofac参数filterattribute悬挂
我有3个类:Apple1
,Apple2
和Apple3
。 Apple2
取决于Apple1
和Apple3
取决于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.tryresole
与task.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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我没有讨论过这个(老实说,没有时间),但是您可能会陷入僵局,因为事物已注册
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 alsoInstancePerLifetimeScope
, 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.