使用 ASP.NET 身份与Ninject 我Startup.Auth.cs
有这个。它主要是样板:
private static StandardKernel _kernel;
private static StandardKernel CreateKernel()
{
if (_kernel == null)
{
System.Diagnostics.Debug.WriteLine("Creating Kernel");
_kernel = new StandardKernel();
_kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
_kernel.Load(new Services.ServiceModule());
}
return _kernel;
}
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
//app.CreatePerOwinContext(CreateKernel);
app.UseNinjectMiddleware(CreateKernel);
app.CreatePerOwinContext<MyApp.Dal.IDataAccessService>(() =>
{
// this occassionally throws a ArgumentNullException
// and _kernel is null in the debugger...?
return _kernel.Get<MyApp.Dal.IDataAccessService>();
});
//... lots more config stuff
}
问题是这行:
return _kernel.Get<MyApp.Dal.IDataAccessService>();
偶尔会抛出ArgumentNullException
,因为_kernel
为空。这似乎发生在我第一次启动我的 Web 应用程序时,特别是如果我在完成加载之前关闭浏览器中的页面并打开另一个页面。我相信这是一个竞争条件,但我可以非常准确地弄清楚这里的事件顺序是什么以及如何正确同步它。
我在违规行周围添加了一个 try catch,以便我可以看到它在哪个线程上执行(并将相同的信息添加到Creating Kernel
调试行,我看到这个:
Creating Kernel on 7
和
Exception thrown: 'System.ArgumentNullException' in Ninject.dll
Exception caught on 8: Cannot be null
很明显,问题在于CreateKernel
与尝试Get
MyApp.Dal.IDataAccessService
实例的线程不同。同步的正确方法是什么?
堆栈跟踪:
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at Ninject.KernelBase.Load(IEnumerable`1 m)
at Ninject.ModuleLoadExtensions.Load(IKernel kernel, INinjectModule[] modules)
at MyApp.Startup.CreateKernel() in C:Usersmatt.burlandDocumentsVisual Studio 2015ProjectsMyAppTrunkMyAppMyAppApp_StartStartup.Auth.cs:line 33
at MyApp.Startup.<>c.<ConfigureAuth>b__3_0() in C:Usersmatt.burlandDocumentsVisual Studio 2015ProjectsMyAppTrunkMyAppMyAppApp_StartStartup.Auth.cs:line 52
at Owin.AppBuilderExtensions.<>c__DisplayClass1`1.<CreatePerOwinContext>b__0(IdentityFactoryOptions`1 options, IOwinContext context)
at Microsoft.AspNet.Identity.Owin.IdentityFactoryProvider`1.Create(IdentityFactoryOptions`1 options, IOwinContext context)
at Microsoft.AspNet.Identity.Owin.IdentityFactoryMiddleware`2.<Invoke>d__0.MoveNext()
尝试使用锁来缓解争用条件。此外,当从表达式调用时,内核在调用时仍有可能为 null,因此请使用该方法以防万一。
private static StandardKernel _kernel;
private static object syncLock = new object();
private static StandardKernel GetKernel() {
if (_kernel == null) {
lock(syncLock) {
if (_kernel == null) {
System.Diagnostics.Debug.WriteLine("Creating Kernel");
_kernel = new StandardKernel();
_kernel.Bind<IHttpModule().To<HttpApplicationInitializationHttpModule>();
_kernel.Load(new Services.ServiceModule());
}
}
}
return _kernel;
}
public void ConfigureAuth(IAppBuilder app) {
// Configure the db context, user manager and signin manager to use a single instance per request
app.UseNinjectMiddleware(GetKernel);
app.CreatePerOwinContext<MyApp.Dal.IDataAccessService>(() =>
{
return GetKernel().Get<MyApp.Dal.IDataAccessService>();
});
//... lots more config stuff
}