How to debug Entity Framework Code First database deployment error? - sql-server

How do I debug an error with my Entity Framework Code First database deployment?
I'm using EF 6.1.3. I'm trying to deploy a database to Azure that includes the following model:
public class DriverLog
{
public int ID { get; set; }
[Required]
public DateTime LogDate { get; set; }
public int RouteID { get; set; }
public virtual Route Route { get; set; }
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public virtual Employee Driver { get; set; }
public int DispatcherID { get; set; }
[ForeignKey("DispatcherID")]
public virtual Employee Dispatcher { get; set; }
}
The deployment succeeds, but when I try to access the database, I get the following error:
{"Message":"An error has occurred."}
If I remove the last two fields -- DispatcherID and Dispatcher -- I do not get this error.
For some reason, EF does not like my including these two fields, but I don't know why, and I don't know how to go about debugging this.

I was able to resolve the problem like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DriverLog>()
.HasRequired(o => o.Dispatcher)
.WithMany()
.WillCascadeOnDelete(false);
}
Credit for this solution goes to: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?

Do check primary key constraint for 'DispatcherID', basicaly the order its creating the table, as you are using 'DropCreateDatabaseIfModelChanges'.
Also inject some custom error catch, to have detail error information.

Related

EF Core running query's I did not call

I have the following two models within my Blazor Server project:
Vergadering:
public class Vergadering
{
[Key]
public int Id { get; set; }
public string Naam { get; set; }
public DateTime DatumTijd { get; set; }
public ICollection<Bestuurslid> Aanwezigen { get; set; }
public string? Notulen { get; set; }
public ICollection<Vergadering>? HoofdVergadering { get; set; }
public ICollection<Vergadering>? GekoppeldeVergaderingen { get; set; }
public ICollection<Bestand>? Bestanden { get; set; }
public string? UserLastEditId { get; set; }
public IdentityUser? UserLastEdit { get; set; }
public DateTime? LastEdit { get; set; }
public ICollection<VergaderingAgendaItem>? vergaderingAgendaItems { get; set; }
}
VergaderingAgendaItem:
public class VergaderingAgendaItem
{
public int Id { get; set; }
public string Omschrijving { get; set; }
public bool Afgerond { get; set; }
public int? ParentId { get; set; }
public VergaderingAgendaItem? Parent { get; set; }
public int VergaderingId { get; set; }
public Vergadering Vergadering { get; set; }
public string? UserAangedragenId { get; set; }
public IdentityUser? UserAangedragen { get; set; }
}
This results in three tables:
Vergaderingen
VergaderingAgendaItems
VergaderingVergadering
In my repository I have the following update method:
public async Task ChangeAfgerondStatusAsync(VergaderingAgendaItem item)
{
using (var _db = _factory.CreateDbContext())
{
_db.VergaderingAgendaItems.Update(item);
await _db.SaveChangesAsync();
}
}
Whenever the Vergadering does not have a GekoppeldeVergadering this update method does not create any problem.
But whenever the Vergadering does have a GekoppeldeVergadering and I update a VergaderingAgendaItem of that Vergadering I get this error:
An error occurred while saving the entity changes. See the inner exception for details.
Looking at the command prompt that opens up while running the project I saw the following query and error.
Queries:
Error:
An exception occurred in the database while saving changes for context type 'AVA_ZICHT.Data.ApplicationDbContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_VergaderingVergadering'. Cannot insert duplicate key in object 'dbo.VergaderingVergadering'. The duplicate key value is (4, 3).
How is it that EF Core tries to update the GekoppeldeVergadering in VergaderingVergadering table. My method states VergaderingAgendaItem.Update()?
When handed a detached entity and told to Update it, EF will consider any associated entities as well. Since those references aren't tracked by the DbContext, the context will see those entities as new items to be inserted. This can result in duplicate key exceptions (as you are seeing) or inserting duplicate data with new PKs if those keys are set up as Identity columns.
One way to get around this issue is to use Automapper configured to just update the columns you expect to change:
public async Task ChangeAfgerondStatusAsync(VergaderingAgendaItem item)
{
using (var _db = _factory.CreateDbContext())
{
var existingItem = _db.VergaderingAgendaItems.Single(x => x.Id == item.Id);
Mapper.Map(item, existingItem);
await _db.SaveChangesAsync();
}
}
Alternatively this can be done manually by copying values from item to existingItem. existingItem is tracked entity so once it's updated, just call SaveChanges. The advantage of this over Update is that the resulting UPDATE SQL statement will only be for any columns that have actually changed, and it won't execute an UPDATE if nothing has actually changed.
This assumes we only want to copy fields from that entity, and none of the child/related entities. If you want to alter the collections/associations then you will need to eager load them and handle these separately. For instance changing the UserLastEdit reference, this is likely something you would want to eager-load so that it can be updated with the current User record.
My general advice is to avoid working with detached entities for concerns like this and instead use POCO view models. The trouble with using detached entities is that these are often incomplete representations of entity state, at worst, something deserialized from view state and cast into an Entity object. View Models can also be scaled down to just the data your client needs and what data is allowed to change. When it gets back to the server there is no confusion about what it is vs. what it pretends to be. Another consideration of applying updates which is important in multi-user systems is detecting stale data. Writing updates like this applies a "last in wins" approach where you should ideally check that the current DB data state concurrency token matches the token/version at the time that this user's original version was read. The attraction of using detached entities is the thought of avoiding a round-trip to the DB when performing an update, but in all honesty you should justify a round trip to ensure that the record is actually valid, the user actually can update that record, and the record hasn't been updated by someone else in the time this user was editing it.

Another efcore violation of primary key constraint reference table problem

Assume I've read and googled, and I still don't know what I'm doing incorrectly. Whenever I try to execute
_dbContext.Set<T>().Add(aMediaObjectWithAssociatedProvider);
_dbContext.SaveChanges();
I get the dreaded efcore violation of primary key constraint reference table
I have a class as such:
public class Media : BaseModel
{
public virtual string Title { get; set; }
public virtual string? Description { get; set; }
public virtual string Source { get; set; }
public virtual Guid? MediaTypeId { get; set; }
public virtual Guid? ProviderId { get; set; }
public virtual DateTime? StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual Provider? Provider { get; set; }
}
The BaseModel class is
public abstract class BaseModel : IBaseModel
{
public virtual Guid Id { get; set; }
}
The Provider class is as such:
public class Provider : BaseModel
{
public virtual string Name { get; set; }
public virtual string? ApiUsername { get; set; }
public virtual string? ConfigurationSection{ get; set; }
}
My DBContext has the following:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Media>().HasKey(x => x.Id);
mb.Entity<Media>().HasOne(p => p.Provider).WithOne().HasForeignKey<Media>(x => x.ProviderId);
}
The code for inserting a new object is as follows:
public T Insert(T oneObject)
{
try
{
// Ensure the entity has an ID
if (oneObject.Id == Guid.Empty)
{
oneObject.Id = Guid.NewGuid();
}
_dbContext.Set<T>().Add(oneObject);
_dbContext.SaveChanges();
}
catch (Exception error)
{
_logger.LogError(error.Message, error);
}
return oneObject;
}
Assume that providers are static, in a sense that they already exist in their table, and I don't want to add new providers when I save media... Media just needs to have a provider.
I know exactly what is happening (the model, after travelling through json back through the api to the server is losing context, but I'm also trying to build a repository type of system where I don't have to build complex save logic for every object. (hence why i'm hand wringing over adding code that loads existing providers).
This problem specifically began rearing its head when I was saving new Media objects into the database with existing Providers. I am still mulling over how to look up children dynamically, but i'm not quite there yet.
I've been at this for so long, i'm about ready to give up on efcore relations and just rebuild the models as single objects, and handle all of the manipulation in javascript. And I don't like this idea.
I know for a fact that there will be questions for more code, but please let me know what. Again, I'm just stepping into .net core / ef core so this code-first is a little confusing for me. Thanks
You may have 2 options to try. Do backup your whole project and database beforehand. Clone your database to another database name. Try these either one option using new cloned database for testing.
No.1
Set "newid()" without quotes in your ID's default value in sql server. So you don't need to use Guid.NewGuid() in code every insert. newid() will auto generate GUID.
No. 2
How about removing primary key from ID (GUID) and then creating new column "UID" (running number) and set UID (running number) as primary key and enable its identity? You need to change all other tables too. And re-link UID each other if you use relationship. This way, your UID will not have existing number when insert.

Entity Framework Core Migration fails in Case of Change to ValueGenerationStrategy

I have annotated some of my model classes' keys with [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
and added a migration.
When running Update-Database I keep getting following error message:
To change the IDENTITY property of a column, the column needs to be dropped and recreated.
I also tried to Update-Database with a complete new database, to no avail as the error is the same.
When changing the identity property in SQL Server Management Studio, I do not get such error but I would like to avoid a mix of code and DB first approach.
Is there a way to either
force the update to drop and recreate the column
or drop and recreate the table ?
EDIT1:
To answer a question from the comments. My model class before looked like:
public partial class MyModel
{
[Key]
public int Id { get; set; }
[MaxLength(70)]
public string Name { get; set; }
public string Description { get; set; }
...
}
This is my model class after adding the annotation:
public partial class MyModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(70)]
public string Name { get; set; }
public string Description { get; set; }
...
}

One-to-one relationship in EF

My models look the following:
public class Job
{
public int Id { get; set; }
public virtual JobResult Result { get; set; }
}
public class JobResult
{
[Key, ForeignKey("Job")]
public int JobId { get; set; }
[Required]
public virtual Job Job { get; set; }
}
As you see the relationship is a required one-to-one.
When I try to create and save a job:
Job job = new Job();
job.Result = new Result();
context.Jobs.add(job);
context.SaveChanges();
I get the following error:
Unable to determine a valid ordering for dependent operations.
Dependencies may exist due to foreign key constraints, model
requirements, or store-generated values.
I don't quite understand the error message. What does it mean and what could be causing this error?
Your case is not valid beacose this is a circular reference. What must be done EF:
INSERT INTO JobResults(....., Job_Id) VALUES (....., #JobId);
EF can not do this becase Job not inserted and does not have Id.
INSERT INTO Jobs(....., JobResult_Id) VALUES (....., #JobResultId);
EF as well can not do this becase JobResult not inserted and does not have Id.
This is a vicious cycle.
You may revise your model. Get rid of one of the links. Example:
public class Job
{
public int Id { get; set; }
public virtual JobResult Result { get; set; }
}
public class JobResult
{
public int Id { get; set; }
public virtual Job Job { get; set; }
}
class JobResultConfiguration : EntityTypeConfiguration<JobResult>
{
public JobResultConfiguration()
{
HasRequired(e => e.Job).WithRequiredPrincipal(e => e.Result); // one-to-one
}
}
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new JobResultConfiguration());
}
}
This will allow you to do:
Job job = new Job();
job.Result = new Result();
context.Jobs.add(job);
context.SaveChanges();
EF make one-to-one relationship: two entity with two entity with identical primary key.
More detail about Fluent Api: https://msdn.microsoft.com/en-us/data/jj591620.aspx
Or your place revised model domain. As you circular links, i would try to get rid of the one link (or from Job or from JobResult)
Your [Required] field is on the wrong element for the type of operation you are performing. Because you are Requiring a JobResult to have a Job, you can Create a JobResult and then create the Dependent Job (which has no requirements), but not the other way around. If you try to create it from the Job end, the Job isn't attached to the JobResult until it is created, but it can't be created until the JobResult is, creating a circular reference.

Unique constraint with EFCodeFirst and SqlCe4

I'm writing an ASP.net MVC3 application using Entity Framework Code First and SqlCe4.
I have been designing several models, and found one that is turning out to be interesting. This is what I have so far:
public class Preference
{
public int PreferenceId { get; set; }
[Required]
public int StudentId { get; set; }
public virtual Student Student { get; set; }
[Required]
public int PresentationId { get; set; }
public virtual Presentation Presentation { get; set; }
[Required]
public int Rank { get; set; }
}
I however, need a unique constraint or index, because for a particular student, I want them to have to have a list of preferences, but the PresentationId needs to be unique for each student. Is there a way to do this via Code First or some validation attribute?
This sounds like I'm going down the branch of a many to many relationship with the Preference object being an intermediary, to add the Rank property. However, I can't seem to find how to make sure that the values are unique. Is the best way really to manually just add a unique index to the database outside of EF?
Currently(ie EF 4.1) EF does not have an Attribute or configuration mechanism to create unique indexes.
However if you are using Database Initializer you can create it manually
public class MyInitializer : IDatabaseInitializer<MyContext>
{
public void InitializeDatabase(MyContext context)
{
if (!context.Database.Exists() || !context.Database.ModelMatchesDatabase())
{
context.Database.DeleteIfExists();
context.Database.Create();
context.ObjectContext.ExecuteStoreCommand("CREATE INDEX IX_Preference_PresentationId ON Preference ( PresentationId )");
}
}
}
Or execute it outside the Initializer.
I'm not sure how long the feature has existed, but in EF5, you can use the CreateIndex and DropIndex method in your Up() and Down() methods. This allows you to create unique indexes, which function almost identically to unique constraints, in EF's own language.
public partial class UniqueIndexMigration
{
public override void Up()
{
// Create new unique index
this.CreateIndex("dbo.TableName", new[] { "Col1", "Col2", "Col3" }, true, "IX_TableName_Col1_Col2_Col3");
}
public override void Down()
{
// Drop unique index
this.DropIndex("dbo.TableName", "IX_TableName_Col1_Col2_Col3");
}
}
The true, seen above, is the parameter that specifies that it's a unique constraint. Sadly, these constraints are not honored when making further migrations later on (EF won't drop them for you if you change the underlying schema), but my hope is that by using the EF API, your code will get an upgrade for free when this feature is finally added.
Going with what Eranga was saying, I ended up making it work similarly, but like this:
I used the following code in my DataContext class:
public class StudentRegistrationContext : DbContext
{
public StudentRegistrationContext()
{
Database.SetInitializer(new StudentRegistrationDatabaseInitializer());
}
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Presenter> Presenters { get; set; }
public DbSet<Presentation> Presentations { get; set; }
}
In the Database Initializer class, I used the following:
public class StudentRegistrationDatabaseInitializer : DropCreateDatabaseIfModelChanges<StudentRegistrationContext>
{
protected override void Seed(StudentRegistrationContext context)
{
base.Seed(context);
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX UX_Preferences_StudentId_PresentationId ON Preferences (StudentId, PresentationId)");
}
}
See answear that implements the Unique constraint with a Custom ValidationAttribute:
https://stackoverflow.com/a/10566485/1133338

Resources