IOptions configurations throws random NullReferenceException - AspNetCore 2.2 - c#

I have an WebApi app targeting net framework 4.6.1 and using AspNetCore 2.2 :
Setup:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<EmailConfig>(Configuration.GetSection("EmailConfig"));
services.AddTransient<IEmailService, EmailService>();
}
Injecting:
public EmailService(IOptions<EmailConfig> emailConfig)
{
_emailConfig = emailConfig.Value;
}
The setup works fine at the first look, but under some load testing I have noticed that I get the following error when the DI container tries to get the IOptions<EmailConfig>:
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Extensions.Configuration.ConfigurationProvider.<>c__DisplayClass9_0.<GetChildKeys>b__0(KeyValuePair`2 kv)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__1.MoveNext()
at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__1.MoveNext()
at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__1.MoveNext()
at System.Linq.Enumerable.<DistinctIterator>d__64`1.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
at Microsoft.Extensions.Configuration.ChainedConfigurationProvider.GetChildKeys(IEnumerable`1 earlierKeys, String parentPath)
at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
at Microsoft.Extensions.Configuration.ConfigurationRoot.GetChildrenImplementation(String path)
at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(Type type, Object instance, IConfiguration config, BinderOptions options)
at Microsoft.Extensions.Configuration.ConfigurationBinder.Bind(IConfiguration configuration, Object instance, Action`1 configureOptions)
at Microsoft.Extensions.Options.ConfigureNamedOptions`1.Configure(String name, TOptions options)
at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
at System.Lazy`1.CreateValue()
Does anyone got trough this type of problem?
Later Edit:
Removing the IOptions<EmailConfig> from the EmailService and injecting the IConfiguration directly and then getting my emailConfigs from IConfiguration manually, works great and solves my problem.
But still, in order to respect the ISP and SoC principles when using configurations, the recommended way to do it by the aspnetcore team is using Options Pattern, but looks like it is not working fine all the time.
I am starting to think that maybe there is a problem with my setup: aspnetcore 2.2 targeting netFramework 4.6.1. I'll try to upgrade to the latest netcore and also change the target to netcore to see if the problem reproduces.

Related

Unable to resolve dependency when running dotnet core app from console

I am writing a client-server application using Dotnet Core web-api as the backend and Angular2 for the frontend. The persistence layer uses EntityFramework Core to access a postgre database. To try my application I added a database seeding class to create some test data to work with. With this class my problem started.
When I run the application from Visual Studio Code everything is working fine. If I start the application from the terminal with "dotnet run" I get the following exception:
Unhandled Exception: System.Exception: Could not resolve a service of type 'OpenGameListApp.Data.DbSeeder' for the parameter 'dbSeeder' of method 'Configure' on type 'OpenGameListApp.Startup'. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: connectionString
at Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty(String value, String parameterName)
at Microsoft.EntityFrameworkCore.NpgsqlDbContextOptionsExtensions.UseNpgsql(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 NpgsqlOptionsAction)
at OpenGameListApp.Startup.<ConfigureServices>b__4_0(DbContextOptionsBuilder options) in /home/noam/projects/dotnet/OpenGameListApp/src/Startup.cs:line 43
at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.DbContextOptionsFactory[TContext](IServiceProvider applicationServiceProvider, Action`2 optionsAction)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Hosting.Internal.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
at OpenGameListApp.Program.Main(String[] args) in /home/noam/projects/dotnet/OpenGameListApp/src/Program.cs:line 15
The code of the Startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services
services.AddMvc();
// Add entity framework
services.AddEntityFrameworkNpgsql()
.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration["Data:DefaultConnection:ConnectionString"]));
// Add database seeder
services.AddSingleton<DbSeeder>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, DbSeeder dbSeeder)
{
// Add logger
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Rewrite all routing urls including the root one to the index.html file by adding the following rules
var rewriteOptions = new RewriteOptions().AddIISUrlRewrite(env.ContentRootFileProvider, "rewrite-rules.xml");
// Add iis url rewrite features to kestrel
// Note: the app.UseRewriter method must be called before app.UseDefaultFiles and app.UseStaticFiles
app.UseRewriter(rewriteOptions);
// In order for your Web app to serve a default page without the user having to fully qualify the URI,
app.UseDefaultFiles();
// Enable service of static files in wwwroot folder
app.UseStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = (context) =>
{
// Disable caching for all static files
context.Context.Response.Headers["Cache-Control"] = Configuration["StaticFiles:Headers:Cache-Control"];
context.Context.Response.Headers["Pragma"] = Configuration["StaticFiles:Headers:Pragma"];
context.Context.Response.Headers["Expires"] = Configuration["StaticFiles:Headers:Expires"];
}
});
// Enable the use of asp.net mvc framework
app.UseMvc();
// Initialize auto mapper
Mapper.Initialize(cfg => cfg.CreateMap<Item, ItemViewModel>());
// Seed database
dbSeeder.SeedIt();
}
The exception goes away if I remove the dbSeeder from the Startup class. I would really appreciate if someone could point out to me what is happening here.
The comment of Martin Ullrich pointed me in the right direction:
it looks like the connection string cannot be read from the configuration since the call to UseNpgsql() throws
The connection string could indeed not be read from the configuration. In my project I have two different appsettings files: appsettings.development.json and appsettings.json. Only the former contains the connection string. When I run the application in Visual Studio Code it starts in debug environment and is therefore able to read the connection string.
Running the application from the terminal with dotnet run however seems to run the application in production environment. After changing the environment used in the terminal to development (export ASPNETCORE_ENVIRONMENT=Development) everything works fine.

Entity Framework 7 no database provider is configured => when migrations are moved to another project

I am new to EF7. I know this is a duplicate question to:
No database providers are configured EF7
But wait before you want to have this question closed... and read on
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddScoped<TestRepository, TestRepository>();
Now I run the dnx ef database update command on the cmd windows in my EF project and get this error:
C:\TGB.DataAccess>dnx ef database update
System.InvalidOperationException: No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services.
bei Microsoft.Data.Entity.Internal.DatabaseProviderSelector.SelectServices(ServiceProviderSource providerSource)
bei Microsoft.Data.Entity.Internal.DbContextServices.<>c__DisplayClass6_0.<Initialize>b__0()
bei Microsoft.Data.Entity.Internal.LazyRef`1.get_Value()
bei Microsoft.Data.Entity.Internal.DbContextServices.get_DatabaseProviderServices()
bei Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_8(IServiceProvider p)
bei Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryService.Invoke(ServiceProvider provider)
bei Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
bei Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass12_0.<RealizeService>b__0(ServiceProvider provider)
bei Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
bei Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
bei Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
bei Microsoft.Data.Entity.Design.Internal.DesignTimeServicesBuilder.Build(DbContext context)
bei Microsoft.Data.Entity.Design.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
bei Microsoft.Data.Entity.Commands.Program.Executor.<>c__DisplayClass7_0.<UpdateDatabase>b__0()
bei Microsoft.Data.Entity.Commands.Program.Executor.Execute(Action action)
No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services.
Now I tried to change the constructor of my ApplicationDbContext according to the solution link I pasted at the top:
Thats my code:
My ApplicationDbContext.cs is actually empty that means nothing I have overridden.
Looking at the base class of the base class there is the overloaded constructor with parameter DbContextOptions but I can not pass anything from my constructor?!
//
// Summary:
// Initializes a new instance of Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext.
//
// Parameters:
// options:
// The options to be used by a Microsoft.Data.Entity.DbContext.
public IdentityDbContext(DbContextOptions options);
What is broken on my side? I am using EF 7 RC1 and dnx 451.
This happens only when you move the ApplicationDbContext/ApplicationUser and the whole Migrations folder to an extra lets say "DataAccess" project. Then everything seems broken.
Create a new ASP.NET Web Application named "WebProject" using Web Application template (Authentication: Individual User Account).
Now add another project to the solution named "DataAccess" (let's say Class Library type though its not relevant)
Now move ApplicationDbContext/ApplicationUser files & migrations folder to DataAccess project.
At this stage the build will be failing, so we need to correct the project.json for correct references.
For DataAccess project, add following dependencies
"dependencies": {
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final",
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final"
}
For WebProject, add DataAccess as dependency. Since DataAccess has reference to above 2 packages, you can remove those references from WebProject.
Build should succeed now and you can launch web application.
How to use ef migrations:
In command line, go to directory of WebProject (and not the DataAccess project).
All the ef commands will work fine from here.
To add new migrations to the Migrations folder of DataAccess project, you need to use -p flag. Like,
dnx ef migrations add Sample -p DataAccess.
The issue arises because the commands are run from cmd within DataAccess project. Your startup class which is initializing all the services is in WebProject. When you try to run commands from DataAccess directory, dnx ef will be recognized since EntityFramework.Commands is referenced but when you try to use the migrations, services are not initialized therefore it will fail throwing above exception.
Try to move your Context class to your "DataAccess" project and override the OnConfiguring method of your Context in your "DataAccess" project then run the migration.
public partial class MyContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(#"Server=****;Database=***;user id=***;password=***");
}
}
Or you can do like followings if you don't want to hard code your connection string. DI will inject the options for you;
public partial class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
}
and on your Startup.cs;
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<WtContext>(options =>
options.UseSqlServer(Configuration["Data:MyDb:ConnectionString"]));
}
and your appsetting.json;
{
"Data": {
"MyDb": {
"ConnectionString": "**********"
}
}
}

add migration failed when DbContext in sperate project

I have a project with different layers: web, services, model, data each one has a different project in the same solution. The application, compiles and runs OK. But when I tried to implement migration I got the following error
dnx . ef migration add MigrationFile
System.InvalidOperationException: No DbContext was found. Ensure that you're using the correct assembly and that the type is neither abstract nor generic.
at Microsoft.Data.Entity.Commands.ContextTool.SelectType(IEnumerable`1 types, String name)
at Microsoft.Data.Entity.Commands.MigrationTool.GetContextType(String name)
at Microsoft.Data.Entity.Commands.MigrationTool.AddMigration(String migrationName, String contextTypeName, String startupAssemblyName, String rootNamespace,String projectDir)
at Microsoft.Data.Entity.Commands.Program.<>c__DisplayClass12_0.<AddMigration>b__0()
at Microsoft.Data.Entity.Commands.Program.Execute(String startupProject, Func`1 invoke)
at Microsoft.Framework.Runtime.Common.CommandLine.CommandLineApplication.Execute(String[] args)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Framework.Runtime.Common.EntryPointExecutor.Execute(Assembly assembly, String[] args, IServiceProvider serviceProvider)
at Microsoft.Framework.ApplicationHost.Program.ExecuteMain(DefaultHost host,String applicationName, String[] args)
at Microsoft.Framework.ApplicationHost.Program.Main(String[] args)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Framework.Runtime.Common.EntryPointExecutor.Execute(Assembly assembly, String[] args, IServiceProvider serviceProvider)
at dnx.host.Bootstrapper.RunAsync(List`1 args, IRuntimeEnvironment env, FrameworkName targetFramework)
at dnx.host.RuntimeBootstrapper.ExecuteAsync(String[] args, FrameworkName targetFramework)
at dnx.host.RuntimeBootstrapper.Execute(String[] args, FrameworkName targetFramework)
I'm using this answer as a reference.
Maybe your project has more than one DbContext or you have not turned on the migrations.
If you have more than one Context, you will want to enable and add migrations for each Context separately:
add-migration -ConfigurationTypeName MyProject.MigrationsFolder.Configuration "migrationName"
This code will add a new Migration based on your Context and using the Configuration class associated to it. The following code will update the database associated with the Configuration class.
update-database -ConfigurationTypeName MyProject.MigrationsFolder.Configuration
the commands must be like this
- dnu restore
cd the project which contain the context path
dnx . ef migration add -c ContextName - s StartupProjectName
try it and if this work let me know, thnx ^^

Microsoft Unity error when resolving repository from console app - NullReferenceException

I'm having problems resolving a repository when calling it from a console app. Everything works fine when running the application (.NET 4, C#, Entity Framework, Unity) as normal, but I've created a standalone console app that will be run from the task scheduler to import feeds. I'm very close to giving up and do a dirty hack and write a script to call a webpage instead of using a console app, but I thought I'd at least try to understand why it isn't working first.
I'm new to both Entity Framework and Unity, so please bear with me and let me know if I've left out any important information.
This is the error I'm getting:
Resolution of the dependency failed, type = "MyNamespace.Domain.Template.IRepository`2[MyNamespace.Domain.Employees.OrganisationalUnit,System.Guid]", name = "(none)".
Exception occurred while: while resolving.
Exception is: NullReferenceException - Object reference not set to an instance of an object.
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)
at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides)
at Microsoft.Practices.Unity.UnityContainerExtensions.Resolve[T](IUnityContainer container, ResolverOverride[] overrides)
at MyNamespace.Infrastructure.FeedDefinition.GetOrganisationalUnit(String name, OrganisationalUnit parent) in C:\FeedDefinition.cs:line 552
This is the console app code:
static void Main(string[] args)
{
if (args.Length > 0)
{
MyNamespace.Appliance.Configuration.Initialise.InitialiseContainer();
ImportFeedProcessor importFeedProcessor = new ImportFeedProcessor();
importFeedProcessor.Run(args[0]);
}
}
And this is where it fails:
IRepository<OrganisationalUnit, Guid> organisationalUnitRepository =
Context.Instance.Container.Resolve<IRepository<OrganisationalUnit, Guid>>();
If anyone can help me understand what's going wrong I'd be very grateful!
UPDATE:
Here's the (important) bits from the initialise class:
public static void InitialiseContainer()
{
UnityContainer container = new UnityContainer();
// Use HttpContext for registering instances against for live
IContext context = HttpContextWrapper.Instance;
// Register the HttpContext as the default context to use
container.RegisterInstance<IContext>(HttpContextWrapper.Instance);
// repositories
container.RegisterType<IRepository<Employee, Guid>, EmployeeRepository>(
new UnityContextLifetimeManager(context),
new InjectionConstructor(true, "OrganisationalUnit.Parent.Parent"));
container.RegisterType<IRepository<OrganisationalUnit, Guid>, EntityFrameworkRepository<OrganisationalUnit, Guid>>(
new UnityContextLifetimeManager(context),
new InjectionConstructor("Parent.Parent"));
// Create and populate a new Unity container from configuration
Context.Instance.Container = container;
}
Is it perhaps the HttpContext that does it?
Thanks,
Annelie
One option you could consider is creating two different Initialise classes (I'm guessing that is your Unity bootstrapper class).
The one you have can be used for your web application.
The other one should be non-web specific. Remove any reference to HttpContext (it won't be available in a Console app) and UnityContextLifetimeManager (assuming this is HttpContext specific as well).

How can I prevent EF "The context cannot be used while the model is being created" errors?

Looking at my Elmah error logs, I am seeing a few InvalidOperationExceptions from Entity Framework that deal with:
The context cannot be used while the model is being created.
This is with the latest EF CodeFirst library from Nuget. The only information I have been able to find on the net is that it is being caused by having data contexts as singletons, which is most certainly not my case. In my Windsor installer, my EF unit of work structure is being registered with:
container.Register(Component.For<IUnitOfWork>()
.ImplementedBy<EFUnitOfWork>()
.LifeStyle
.PerWebRequest);
I am able to recreate the error by hitting F5 in VS to start a debugging sessions, and while IIS is spinning up load up a second webpage to the debug session.
I suspect it is because the user is trying to access the system while Asp.net has unloaded due to the lack of activity, which makes sense as my product is currently in a very very small beta test. However, since real people are using the website with live data, I need as little errors occurring as possible.
Does anyone have any idea how to prevent this from occurring?
Edit: I updated my windsor controller to now contain the following code:
container.Register(Component.For<IUnitOfWork>().ImplementedBy<EFUnitOfWork>().LifeStyle.PerWebRequest);
using (var context = new MyJobLeadsDbContext())
{
context.Set<UnitTestEntity>().Any();
}
However, when I attempt to perform a 2nd web request while IIS is loading the application, the previous error still occurs
Edit 2: As requested, here is the stack
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.InternalContext.Initialize()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
at MyApp.DomainModel.Queries.Users.UserByEmailQuery.Execute() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.DomainModel\Queries\Users\UserByEmailQuery.cs:line 44
at MyApp.Infrastructure.MyAppMembershipProvider.GetUser(String email, Boolean userIsOnline) in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp\Infrastructure\MyAppMembershipProvider.cs:line 102
at System.Web.Security.Membership.GetUser(String username, Boolean userIsOnline)
at System.Web.Security.Membership.GetUser()
at MyApp.MyAppBaseController.Initialize(RequestContext requestContext) in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp\MyAppBaseController.cs:line 23
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.<EndProcessRequest>b__d()
at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
I finally figured out the true cause of this, at least for me.
The issue was that I was retrieving a DbContext from Windsor in my custom Asp.Net Membership provider. This caused an issue because the membership provider has a lifespan of the whole application, while all other retrieval calls for the db context were new db contexts for the specific web requests. This meant that two database contexts were "spinning up" at the same time and thus this error was thrown.
This also caused a lot of hard to debug entity caching issues as well, so anyone who uses EF in their membership provider needs to be real careful about their context lifetime.
Edit: In response to DotNetWise, I solved this by forcing my custom membership provider to always use an EF connection from Windsor by storing the Windsor connection factory in my constructor, then always retrieving my EF data context from the factory at that point.
For example:
public class CustomMembershipProvider : MembershipProvider
{
private IServiceFactory _serviceFactory;
public CustomMembershipProvider() : this(null) { }
public CustomMembershipProvider(IServiceFactory factory)
{
// IF no factory was provided, we need to get one from the bootstrapper
if (factory == null)
_serviceFactory = new WindsorServiceFactory(Bootstrapper.WindsorContainer);
else
_serviceFactory = factory;
}
public override string ResetPassword(string email, string answer)
{
var unitOfWork = GetUnitOfWork();
return new ResetUserPasswordCommand(unitOfWork).WithUserEmail(email).Execute();
}
private IUnitOfWork GetUnitOfWork()
{
return _serviceFactory.GetService<IUnitOfWork>();
}
}
The idea being that any action the membership provider performs gets the UnitOfWork class from Windsor, and uses that to perform the action (in this case my UnitOfWork class is a repository holder to wrap up my EF data context)
I ran into the same issue in a multithreaded WPF app.
My workaround was to force DbContext initialization from the Windsor installer:
container.Register(Component.For(TheDbContext.Blah.Blah));
using (var context = new TheDbContext())
context.Set<SomeRandomEntity>().Any();
I might add in my opinion this qualifies as a bug in EF: they should have used thread-safe (with locks, or whatever) code for DbContext initialization.
Of course, a better solution is what NHibernate does: the SessionFactory is an explicitly created, separate object from the Session.
When i encountered this issue, i found it was a dbconnection gone wrong.
I corrected my EntityFramework dbconnection string and all was fine

Categories