#SharePointProblems | Koskila.net

Solutions are worthless unless shared! Antti K. Koskela's Personal Professional Blog
>

The easiest way to debug Seed-method in Code-first migrations in Entity Framework

koskila
Reading Time 5 min
Word Count 857 words
Comments 0 comments
Rating 4 (2 votes)
View

This post describes the easiest way to debug the issues that may stop your Seed-method in Configuration.cs from going through. This problem concerns typically your ASP.NET MVC projects, either on .NET Framework or .NET Core - the same basic idea should work for both situations.

The solution here shows you, how you can get a little bit more information out of the process, without attaching the debugger (there's another blog post for that: How to launch the visual studio debugger from code/)

Description

Entity Framework's code-first migrations are a beautiful and easy way of managing database schema changes and populating some preliminary data there. Personally I also sometimes use the method for adding some enrichment to data or custom property values mapping that would otherwise require an additional/external console program.

Problem: running the Seed-method is by default undebuggable

Okay - so seeding data is cool. You do that within your Configuration.cs -file when running Update-Database to insert some default or baseline data to your database.

That's fine and dandy, but debugging the issues in the function while running Update-Database is NOT so cool. You can't run the Seed-method with the debugger turned on. Hence, when you run into issues, you only get the stacktrace and exception message - and that's not always that informative.

In my other blog post, I describe how you CAN in fact debug the Seed-method, by launching a debugger instance manually from the method itself. See the other solution here:

https://www.koskila.net/visual-studio-debugger-instance-from-code/

This blog post shows an alternative to that - you can also modify the error messages thrown during the Seed method!

See this:

update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
No pending explicit migrations.
Running Seed method.
System.InvalidOperationException: Sequence contains more than one element
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.b__2[TResult](IEnumerable`1 sequence)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
   at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[TResult](Expression expression)
   at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
   at System.Data.Entity.Migrations.DbSetMigrationsExtensions.AddOrUpdate[TEntity](DbSet`1 set, IEnumerable`1 identifyingProperties, InternalSet`1 internalSet, TEntity[] entities)
   at System.Data.Entity.Migrations.DbSetMigrationsExtensions.AddOrUpdate[TEntity](IDbSet`1 set, Expression`1 identifierExpression, TEntity[] entities)
   at ....Migrations.Configuration.Seed(ApplicationDbContext db) in C:\...\Migrations\Configuration.cs:line 345
   at System.Data.Entity.Migrations.DbMigrationsConfiguration`1.OnSeed(DbContext context)
   at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
   at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.DbMigrator.UpdateInternal(String targetMigration)
   at System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClassc.b__b()
   at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
   at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)
   at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()
   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
Sequence contains more than one element.

Okay, so "Sequence contains more than one element". Cool. I'm probably failing an AddOrUpdate-call somewhere, but I hate going through the exception to find the Configuration.cs:345 (or similar) row that actually tells where the exception was when I could have the exception tell me what went wrong. Unfortunately, the exception just shows me the message of the thrown exception - which is NOT descriptive.

Oh, but wait: There is a solution!

Solution

Time needed: 15 minutes.

By wrapping the whole stuff in a try-catch-block, catching the thrown exception, wrapping a new exception with a custom message around it, and including the original one as an internal exception, we get the data from the original exception AND our custom message - which in my case tells how far in the Seed-method (or just in which part of seeding/mapping the data we are in) we've proceeded. You can even make it tell the line where we've gotten to!

  1. So, in my Configuration.cs class (in Migrations), I have this first:

    <pre lang="csharp">internal class Configuration : DbMigrationsConfiguration<applicationdbcontext>     {         private bool _debug = false;           private string _customExceptionMessage = "";   ... </applicationdbcontext></pre>

  2. Then, further down the line when seeding my data, I have these little calls:

    <pre lang="csharp">db.Companies.AddOrUpdate(p => p.CompanyIdentifier,                 new Company                 {                                 ...                 },                 new Company                 {                                 ...                 } ); db.SaveChanges(); _customExceptionMessage += "Companies saved ok;"; </pre>

  3. This goes on for a while, but I've wrapped the whole method in a try-catch, like below:

    <pre lang="csharp">/// <summary> /// This method will be called after migrating to the latest version. /// </summary> /// <param name="db"> protected override void Seed(ApplicationDbContext db) {                 try                 {                                 _customExceptionMessage += "Running Seed method in Configuration.cs;";                                   db.Companies.AddOrUpdate(p => p.CompanyIdentifier,                                                 new Company                                                 {                                                                 ...                                                 },                                                 new Company                                                 {                                                                 ...                                                 }                                 );                                 db.SaveChanges();                                 _customExceptionMessage += "Companies saved ok;"                                                                 ...                 }                 catch (Exception ex)                 {                                 Exception ex2 = new Exception(_customExceptionMessage, ex);                                   throw ex2;                 } } </pre>

  4. Now the exception in the Package Manager Console will be something like this:

    <pre lang="csharp">// Omitted for clarity ...    at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)    at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)    at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()    at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)    at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)    at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)    at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)    at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()    at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command) Running Seed method in Configuration.cs;Companies saved ok;User added to company ok;Contacts saved ok;</pre>

  5. So, now you get this:

    <pre>Running Seed method in Configuration.cs;Companies saved ok;User added to company ok;Contacts saved ok;</pre>

To let you know how far you got in the execution.

This is, at least to me, a small improvement. Pretty? Ha, no. Much faster than launching a debugger? Yes!

Also, very hack-ish, but better than the normal functionality. And combined with launching the debugger from the code (as described here), quite an improvement over just getting a stacktrace.

Comments

Interactive comments not implemented yet. Showing legacy comments migrated from WordPress.

No comments yet.