0

I'm dropping database, creating it again and Seeding with data.

With this type of registry everything works fine

services.AddDbContext<Context>
(
   options => options.UseSqlServer(Configuration["Database"]
);

But, when I add ServiceLifetime.Transient then instantly it throws various exceptions like:

services.AddDbContext<Context>
(
   options => options.UseSqlServer(Configuration["Database"], ServiceLifetime.Transient
);

SqlException: Violation of PRIMARY KEY constraint 'PK_myTable'. Cannot insert duplicate key in object 'dbo.myTable'. The duplicate key value is ( [guid] ). The statement has been terminated.

SqlException: Cannot insert explicit value for identity column in table 'myTable' when IDENTITY_INSERT is set to OFF.

What may be wrong?

public void Configure(IApplicationBuilder app, IHostingEnvironment env, Context context, SignInManager<User> sm, UserManager<User> um)
{
    (...)
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();
    new Initializer(context, um).Initialize().Wait();
    (...)
}

public class Initializer
{
    private readonly UserManager<User> _userManager;
    private readonly Context _ctx;

    public Initializer(Context c, UserManager<User> um)
    {
        _ctx = c;
        _userManager = um;
    }

    public async Task Initialize()
    {
        filling my objects with data

        (No, I'm not setting value to Id anywhere)
        _ctx.SaveChanges();
    }

I bet that's because of async / .Wait()?

Joelty
  • 1,751
  • 5
  • 22
  • 64
  • Can you show the code that drops/creates and feeds the data? – juunas Nov 06 '18 at 10:04
  • @juunas Added the code – Joelty Nov 06 '18 at 10:09
  • 2
    Aha, it might be because UserManager is getting a different context instance. Remember that Transient means each object that requires it will get their own instance each time. – juunas Nov 06 '18 at 10:17
  • @juunas even in startup? – Joelty Nov 06 '18 at 10:18
  • 2
    Yes, every single time an instance is asked from the DI container, a new instance is created (if it is Transient). – juunas Nov 06 '18 at 10:18
  • @juunas So, whenever I want to recreate + seed database I gotta swap those, or just create another method in startup.cs instead of creating whole, dedicated ``Initializer`` class? – Joelty Nov 06 '18 at 10:20
  • One thing is that you should not put anything across UserManager <-> Context, as their state is different. If you use them 100% separately in the Initialize function, they should be fine. – juunas Nov 06 '18 at 10:29
  • Why are you wanting your context transient in the first place? – Chris Pratt Nov 06 '18 at 15:46
  • @ChrisPratt Because it solved other problem for me: https://stackoverflow.com/questions/53150930/how-to-avoid-not-safe-context-operations-in-ef-core – Joelty Nov 07 '18 at 08:15
  • 1
    That previous issue is almost certainly down to using async without awaiting, which is a problem that you actually need to solve, not just side-step by making the lifetime transient. Regardless, your context should not be transient for a whole host of reasons, this issue now being one. Leave it in request scope and fix your real problem. – Chris Pratt Nov 07 '18 at 12:10
  • @ChrisPratt Well, apparently removing asyncs was the easiest solution :) Thanks – Joelty Nov 07 '18 at 12:21
  • Not to harp on the issue, but that is yet another side-step. All EF Core operations are async. The sync methods block on the async one. You shouldn't use the sync methods unless you're in a situation where you have no other choice. You can keep using async *and* solve your error, and that is the course you should be taking. – Chris Pratt Nov 07 '18 at 12:25
  • @ChrisPratt Oh, seems like that was temporary workaround... So, what may be the problem here? ``One thing is that you should not put anything across UserManager <-> Context, as their state is different. If you use them 100% separately in the Initialize function, they should be fine.``? – Joelty Nov 07 '18 at 13:02

1 Answers1

0

Solution:

I removed

ServiceLifetime.Transient

and then in my Initializer class I added .Wait() to

userManager.CreateAsync();

Because I've been creating users dynamically.

and it stopped throwing exceptions about context being used in other thread.

or generally await it?

Joelty
  • 1,751
  • 5
  • 22
  • 64