我试图让Unity管理我的对象的创建,我希望有一些在运行时才知道的初始化参数:
目前,我能想到的方法是在接口上使用Init方法.
interface IMyIntf {
void Initialize(string runTimeParam);
string RunTimeParam { get; }
}
Run Code Online (Sandbox Code Playgroud)
然后使用它(在Unity中)我会这样做:
var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");
Run Code Online (Sandbox Code Playgroud)
在这种情况下,runTimeParamparam是在运行时根据用户输入确定的.这里的简单案例只返回值,runTimeParam但实际上参数将类似于文件名,初始化方法将对文件执行某些操作.
这会产生许多问题,即该Initialize方法在界面上可用并且可以多次调用.在实现中设置一个标志并在重复调用时抛出异常Initialize似乎很笨重.
在解析我的界面时,我不想知道有关实现的任何信息IMyIntf.但是,我想要的是知道这个接口需要一定的一次初始化参数.有没有办法以某种方式注释(属性?)具有此信息的接口,并在创建对象时将它们传递给框架?
编辑:更多地描述了界面.
interface-design dependency-injection ioc-container inversion-of-control unity-container
我有一个服务,它在一个名为的方法中从文件中异步读取一些内容 InitAsync
public class MyService : IService {
private readonly IDependency injectedDependency;
public MyService(IDependency injectedDependency) {
this.injectedDependency = injectedDependency;
}
public async Task InitAsync() {
// async loading from file.
}
}
Run Code Online (Sandbox Code Playgroud)
现在这个服务被注入到我的控制器中。
public class MyController : Controller {
private readonly IService service;
public MyController(IService service) {
this.service = service;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我想要一个 MyService 的单例实例。我想在启动时调用 InitAsync。
public class Startup {
public void ConfigureServices(IServiceCollection services) {
......
services.AddSingleton<IService, MyService>();
var serviceProvider = services.BuildServiceProvider();
// perform async init.
serviceProvider.GetRequiredService<IService>().InitAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
发生的事情是在启动时,创建并 …
更新:@usr指出我错误地认为Lazy<T>默认的线程安全模式是LazyThreadSafetyMode.PublicationOnly......
我想懒洋洋地通过async工厂方法计算一个值(即它返回Task<T>)并在成功时缓存它.在例外情况下,我想让我可以使用它.我不然而,要堕入了异常缓存的行为是Lazy<T>在其默认模式(LazyThreadSafetyMode.ExecutionAndPublication)
异常缓存:使用工厂方法时,会缓存异常.也就是说,如果工厂方法在线程第一次尝试访问Lazy对象的Value属性时抛出异常,则每次后续尝试都会抛出相同的异常.这确保了对Value属性的每次调用都会产生相同的结果,并避免在不同的线程获得不同结果时可能出现的细微错误.懒惰代表一个实际的T,否则它将在某些早期点(通常在启动期间)初始化.在那个早期点失败通常是致命的.如果存在可恢复故障的可能性,我们建议您将重试逻辑构建到初始化例程(在本例中为工厂方法),就像您不使用延迟初始化一样.
Stephen Toub的AsyncLazy课程和写作似乎恰到好处:
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
{ }
public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}
Run Code Online (Sandbox Code Playgroud)
然而,这实际上与默认行为相同Lazy<T>- 如果出现问题,则不会重试.
我正在寻找一个Task<T>兼容的等价物Lazy<T>(Func<T>, LazyThreadSafetyMode.PublicationOnly),即它应该按照指定的行为: -
锁定的替代方法在某些情况下,您可能希望避免Lazy对象的默认锁定行为的开销.在极少数情况下,可能存在死锁的可能性.在这种情况下,您可以使用Lazy(LazyThreadSafetyMode)或Lazy(Func,LazyThreadSafetyMode)构造函数,并指定LazyThreadSafetyMode.PublicationOnly.如果线程同时调用Value属性,这使Lazy对象能够在多个线程中的每个线程上创建一个延迟初始化对象的副本.Lazy对象确保所有线程使用延迟初始化对象的相同实例并丢弃未使用的实例.因此,降低锁定开销的成本是您的程序有时可能会创建并丢弃昂贵对象的额外副本.在大多数情况下,这不太可能.Lazy(LazyThreadSafetyMode)和Lazy(Func,LazyThreadSafetyMode)构造函数的示例演示了此行为.
重要
指定PublicationOnly时,即使指定了工厂方法,也不会缓存异常.
是否有任何FCL Nito.AsyncEx或类似的结构可能适合这里?如果没有这个,任何人都可以看到一种优雅的方式来控制"尝试进行中"位(我很好,每个调用者以与Lazy<T>(... 相同的方式进行自己的尝试(LazyThreadSafetyMode.PublicationOnly))并且仍然具有该缓存管理封装整齐?
我只是想知道async/await在DI期间是否有可能.
执行以下操作,DI无法解析我的服务.
services.AddScoped(async provider =>
{
var client = new MyClient();
await client.ConnectAsync();
return client;
});
Run Code Online (Sandbox Code Playgroud)
以下作品完美无缺.
services.AddScoped(provider =>
{
var client = new MyClient();
client.ConnectAsync().Wait();
return client;
});
Run Code Online (Sandbox Code Playgroud) c# dependency-injection async-await asp.net-core-mvc asp.net-core-webapi
TL;DR:Autofac 支持类似 AutoFixture 的fixture.Get()机制吗?
我正在使用 Autofac 并且需要调用异步工厂方法,如下所示:
class AppModel
{
public static async Task<AppModel> CreateAsync(IDependency x, IDependency2 y)
{ ... }
}
Run Code Online (Sandbox Code Playgroud)
对我来说执行此类方法并由 Autofac 提供参数的最简单方法是什么?即,我希望能够做类似的事情:
Task<AppModel> creationTask = <some autofaccery>(AppModel.CreateAsync);
var appModel = await creationTask();
Run Code Online (Sandbox Code Playgroud)
其中<some autofaccery>表示与ContainerBuilder和/或IContainer和/或某种形式的生成的委托或类似的交互的某种机制,它本质上是简洁的,并且使我无法显式指定工厂方法的参数。即,我想避免像 atm 那样显式解析每个参数 [和/或必须在依赖项发生变化时更新它们]:
var appModel = await AppModel.CreateAsync(
container.Resolve<IDependency>(),
container.Resolve<IDependency2>());
Run Code Online (Sandbox Code Playgroud)
我位于基础设施组件领域,靠近组合根,可能会以编程方式定义组件注册和/或执行其他应限制在此处的肮脏操作。我不介意参与反射,因为它只被调用一次。
关键是我确实需要观察Exception所产生的任何结果Task。
这Task<T>在很大程度上是一个转移注意力的话题,但重点是,遵循定义同步工厂方法并让 Autofac 完成工作的正常模式不会成功(至少不会直接),即我不能只是改变它到:
public static AppModel CreateAsync(IDependency x, IDependency2 y)
{ ... } …Run Code Online (Sandbox Code Playgroud) 我正在配置Quartz库以使用Simple Injector,我有正确注册它的问题.问题是,GetScheduler()是异步这个代码注册Task<IScheduler>不IScheduler
container.Register(async () =>
{
return await container.GetInstance<ISchedulerFactory>().GetScheduler();
});
Run Code Online (Sandbox Code Playgroud)
如何在Simple Injector中注册异步的工厂方法?当然,我可以等使用效果Result的Task,但也许还有其他办法吗?
我的印象是依赖注入对不变性并不友好。我使用Microsoft 依赖注入,但我对另一个依赖注入持开放态度,我希望尽可能多地读取字段/属性,因此有一个构造阶段,然后是 init 阶段,这是最好的解决方案。当我需要创建一个通过互联网获取数据的类时,我的问题出现了,我想要这样的东西:
var services = new ServiceCollection();
services.AddSingleton<IMyWebService, MyWebService>();
services.AddSingleton<IMyOtherService, MyOtherService>(async sp =>
{
var webService = sp.GetService<IMyWebService>();
return new MyOtherService(await webService.GetDataAsync());
});
Run Code Online (Sandbox Code Playgroud)
使其同步将使我的应用程序无响应。
c# ×6
async-await ×3
asynchronous ×2
.net ×1
asp.net-core ×1
autofac ×1
factory ×1
immutability ×1
quartz.net ×1
task ×1