I have several related domain models that are triggering the exception SqlException: Invalid column name 'ChecklistTemplate_Id'.
My domain model looks like:
public class Assignment
{
public long Id { get; set; }
public long ChecklistId { get; set; }
public DateTime InspectionDate { get; set; }
public long JobId { get; set; }
public Guid? EmployeeId { get; set; }
// TODO: Make the completion a nullable date time in the database and here
public DateTime CompletionDate { get; set; }
public virtual Job Job { get; set; }
public virtual Checklist Checklist { get; set; }
public virtual IList<Image> Images { get; set; }
public virtual IList<Attachment> Attachments { get; set; }
public virtual IList<Equipment> Equipments { get; set; }
}
My EntityTypeConfiguration class looks like:
internal class AssignmentConfiguration : EntityTypeConfiguration<Assignment>
{
public AssignmentConfiguration()
{
ToTable("Assignment");
HasKey(k => k.Id);
Property(a => a.ChecklistId)
.IsRequired();
Property(a => a.CompletionDate)
.IsOptional();
Property(a => a.EmployeeId)
.IsOptional();
Property(a => a.Id)
.IsRequired();
Property(a => a.InspectionDate)
.IsRequired();
Property(a => a.JobId)
.IsRequired();
HasRequired(a => a.Job)
.WithMany(a => a.Assignments)
.HasForeignKey(a => a.JobId);
HasRequired(a => a.Checklist)
.WithOptional(a => a.Assignment);
HasMany(a => a.Images)
.WithRequired(a => a.Assignment)
.HasForeignKey(a => a.InspectionId);
}
}
The Checklist domain model has a ChecklistTemplate navigation property with the join:
HasMany(a => a.CheckLists)
.WithRequired(a => a.ChecklistTemplate)
.HasForeignKey(a => a.ChecklistTemplateId);
There is a one to one between Assignment and Checklist as seen in the Assignment entity configuration.
And yes, we are including the configuration in the DBContext.
Also, I looked at Entity Framework 6 creates Id column even though other primary key is defined and that doesn't seem to apply.
I dont have a satisfactory answer to that but I have had a lot of trouble with ef6. This is because there is a navigation which is not defined or wrongly defined. So ef6 creates it on-the-fly on proxy classes and you cry for hours. I hope you find out the problem soon.
And the navigation you stated is one-to-many. Be careful.
Related
I have complicated relationships between these entities:
Country
Airport
Airline
Flight
Country has many Airlines and many Airports.
Airline has one Countries, the same about Airport.
Airline has many Flights,
Airport has many DepartureFlights and ArrivalFlights (both are Flight type).
Flight has one Airline and one DepartureAirport and one ArrivalAirport (both are Airport type).
Country can have no airlines and airports,
Airline can have no Flights,
Airport can have neither DepartureFlights nor ArrivalFlights.
What I am trying to do is when Country is deleted, then all related Airlines and Airports are deleted, also when Airline or DepartureAirport or ArrivalAirport are deleted, all related Flights are deleted also,
but when updating my db after the migration is created I'm getting an error:
Introducing FOREIGN KEY constraint "FK_Flights_Airports_DepartureAirport" on table "Flights" may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
How to implement this behavior and prevent an error?
Here are my models:
Country:
public class Country
{
public int Id { get; set; }
/* other properties */
public virtual ICollection<Airport>? Airports { get; set; }
public virtual ICollection<Airline>? Airlines { get; set; }
}
Airline:
public class Airline
{
public int Id { get; set; }
/* other properties */
public int CountryId { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Flight>? Flights { get; set; }
}
Airport:
public class Airport
{
public int Id { get; set; }
public string Name { get; set; }
/* other properties */
public int CountryId { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Flight>? DepartureFlights { get; set; }
public virtual ICollection<Flight>? ArrivalFlights { get; set; }
}
Flight:
public class Flight
{
public int Id { get; set; }
/* other properties */
public int AirlineId { get; set; }
public int DepartureAirportId { get; set; }
public int ArrivalAirportId { get; set; }
public virtual Airline Airline { get; set; }
public virtual Airport DepartureAirport { get; set; }
public virtual Airport ArrivalAirport { get; set; }
}
After all the DBContext file:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Airline> Airlines { get; set; }
public DbSet<Airport> Airports { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Flight> Flights { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Airline>(x =>
{
x.HasKey(a => a.Id);
x.HasOne(c => c.Country)
.WithMany(a => a.Airlines)
.HasForeignKey(a => a.CountryId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity<Airport>(x =>
{
x.HasKey(a => a.Id);
x.HasOne(c => c.Country)
.WithMany(a => a.Airports)
.HasForeignKey(a => a.CountryId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity<Country>(x =>
{
x.HasKey(c => c.Id);
});
modelBuilder.Entity<Flight>(x =>
{
x.HasKey(f => f.Id);
x.HasOne(a => a.Airline)
.WithMany(f => f.Flights)
.HasForeignKey(f => f.AirlineId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
x.HasOne(a => a.DepartureAirport)
.WithMany(f => f.DepartureFlights)
.HasForeignKey(f => f.DepartureAirportId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
x.HasOne(a => a.ArrivalAirport)
.WithMany(f => f.ArrivalFlights)
.HasForeignKey(f => f.ArrivalAirportId)
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
}
}
How to implement this behavior and prevent an error?
This is officially known issue. Therefore, We have two ways to handle this scenario thus the error:
1. Change one or more of the relationships to not cascade delete.
In this scenario, we could make the Country relationship with Airport, Flight and Airlines optional by giving it a nullable foreign key property: for instance we can do something like:
.IsRequired(false);
Note: You can check our official document for more details.
2. Configure the database without one or more of these cascade deletes,
then ensure all dependent entities are loaded so that EF Core can
perform the cascading behavior.
Considering this appreach we can keep the Airport, Flight and Airlines relationship required and configured for cascade delete, but make this configuration only apply to tracked entities, not the database: So we can do somethng like below:
modelBuilder.Entity<Airline>(x =>
{
x.HasKey(a => a.Id);
x.HasOne(c => c.Country)
.WithMany(a => a.Airlines)
.HasForeignKey(a => a.CountryId)
.OnDelete(DeleteBehavior.ClientCascade);
.IsRequired(false);
});
modelBuilder.Entity<Airport>(x =>
{
x.HasKey(a => a.Id);
x.HasOne(c => c.Country)
.WithMany(a => a.Airports)
.HasForeignKey(a => a.CountryId)
.OnDelete(DeleteBehavior.ClientCascade);
.IsRequired(false);
});
Note: You can apply same for Flight as well. In addition, As you may know OnDelete(DeleteBehavior.ClientCascade); or ClientCascade allows the DBContext to delete entities even if there is a cyclic ref or LOCK on it. Please read the official guideline for more details here
add these lines in "OnModelCreating"
var cascades = modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetForeignKeys())
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
foreach (var fk in cascades)
fk.DeleteBehavior = DeleteBehavior.Restrict;
I have an ApplicationUser model:
public class ApplicationUser : IdentityUser
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public List<Project> Projects { get; set; }
}
... and a Project model:
public class Project
{
public int Id { get; set; }
public int? ParentProjectId { get; set; }
[Required]
public string ProjectCreatorId { get; set; }
[Required]
public string ProjectOwnerId { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public ApplicationUser ProjectCreator { get; set; }
public ApplicationUser ProjectOwner { get; set; }
public Project ParentProject { get; set; }
public virtual List<Project> ChildProjects { get; set; }
}
In OnModelCreating(), I tried this:
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Project>()
.HasMany(c => c.ChildProjects)
.WithOne(p => p.ParentProject)
.HasForeignKey(p => p.ParentProjectId);
modelBuilder.Entity<ApplicationUser>()
.HasMany(p => p.Projects)
.WithOne(o => o.ProjectOwner)
.HasForeignKey(po => po.ProjectOwnerId);
modelBuilder.Entity<ApplicationUser>()
.HasMany(p => p.Projects)
.WithOne(c => c.ProjectCreator)
.HasForeignKey(pc => pc.ProjectCreatorId);
But upon creating the database, I get
Cannot create a relationship between 'ApplicationUser.Projects' and 'Project.ProjectCreator', because there already is a relationship between 'ApplicationUser.Projects' and 'Project.ProjectOwner'. Navigation properties can only participate in a single relationship.
I tried the solutions to this old question, but wasn't able to make any of them work.
Is there another way I could keep track of both a Project's creator AND owner, and be able to .Include() them both in the queries?
Instead of different binding, use the single binding
modelBuilder.Entity<ApplicationUser>()
.HasMany(p => p.Projects)
.WithOne(o => o.ProjectOwner)
.HasForeignKey(po => po.ProjectOwnerId);
.WithOne(c => c.ProjectCreator)
.HasForeignKey(pc => pc.ProjectCreatorId);
I went for this solution:
As mentioned in the comments, ApplicationUser would need two List<Project>-properties:
public List<Project> CreatedProjects { get; set; }
public List<Project> OwnedProjects { get; set; }
Then, in OnModelCreating():
modelBuilder.Entity<Project>()
.HasOne(g => g.ProjectCreator)
.WithMany(t => t.CreatedProjects)
.HasForeignKey(t => t.ProjectCreatorId)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Project>()
.HasOne(g => g.ProjectOwner)
.WithMany(t => t.OwnedProjects)
.HasForeignKey(t => t.ProjectOwnerId).OnDelete(DeleteBehavior.NoAction)
.HasPrincipalKey(t => t.Id);
Now I'm able to include both creators and owners. :)
I want to create database EF migrations via the developer command prompt for VS2015. When I try to use this command line:
dotnet ef migrations add v1
I get this error:
The property 'partCategoriess' cannot be added to the entity type
'PartCategoryPart' because a navigation property with the same name
already exists on entity type 'PartCategoryPart'.
Is anything wrong with the DbContext? I am trying to create a many-to-many table between categoryParts and parts.
public class ShoppingDbContext : IdentityDbContext<User>
{
public ShoppingDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
public DbSet<PartCategory> PartCategories { get; set; }
public DbSet<Part> Parts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PartCategoryPart>()
.HasKey(t => new { t.partCategoriess, t.Part });
modelBuilder.Entity<PartCategoryPart>()
.HasOne(pt => pt.partCategoriess)
.WithMany(p => p.PartCategoryPart)
.HasForeignKey(pt => pt.PartCategoryId);
modelBuilder.Entity<PartCategoryPart>()
.HasOne(pt => pt.Part)
.WithMany(t => t.PartCategoryPart)
.HasForeignKey(pt => pt.PartId);
}
}
public class PartCategoryPart
{
public int Id { get; set; }
public int PartCategoryId { get; set; }
public PartCategory partCategoriess { get; set; }
public int PartId { get; set; }
public Part Part { get; set; }
}
public class PartCategory
{
public int PartCategoryId { get; set; }
public string Category { get; set; }
public List<ProductPartCategory> ProductPartCategories { get; set; }
public List<PartCategoryPart> PartCategoryPart { get; set; }
}
public class Part
{
public int PartId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public double? Price { get; set; }
public List<PartCategoryPart> PartCategoryPart { get; set; }
}
The problem here is how you are defining the primary key for the PartCategoryPart intermediate entity. You are using the navigation properties to define the PK and you have to use the FKs like this:
modelBuilder.Entity().HasKey(t => new { t.PartCategoryId, t.PartId});
Referring to my own assignment, here's how you properly create an entity. You did not define a key..
modelBuilder.Entity<Price>()
.HasKey(input => input.PriceId)
.HasName("PrimaryKey_Price_PriceId");
// Provide the properties of the PriceId column
modelBuilder.Entity<Price>()
.Property(input => input.PriceId)
.HasColumnName("PriceId")
.HasColumnType("int")
.UseSqlServerIdentityColumn()
.ValueGeneratedOnAdd()
.IsRequired();
//modelBuilder.Entity<Price>()
// .Property(input => input.MetricId)
// .HasColumnName("MetricId")
// .HasColumnType("int")
// .IsRequired();
modelBuilder.Entity<Price>()
.Property(input => input.Value)
.HasColumnName("Value")
.HasColumnType("DECIMAL(19,4)")
.IsRequired();
modelBuilder.Entity<Price>()
.Property(input => input.RRP)
.HasColumnName("RRP")
.HasColumnType("DECIMAL(19,4)")
.IsRequired(false);
modelBuilder.Entity<Price>()
.Property(input => input.CreatedAt)
.HasDefaultValueSql("GetDate()");
modelBuilder.Entity<Price>()
.Property(input => input.DeletedAt)
.IsRequired(false);
// Two sets of Many to One relationship between User and ApplicationUser entity (Start)
modelBuilder.Entity<Price>()
.HasOne(userClass => userClass.CreatedBy)
.WithMany()
.HasForeignKey(userClass => userClass.CreatedById)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
modelBuilder.Entity<Price>()
.HasOne(userClass => userClass.DeletedBy)
.WithMany()
.HasForeignKey(userClass => userClass.DeletedById)
.OnDelete(DeleteBehavior.Restrict);
Notice that after identifying which is the key you still need to declare its properties before you declare any relationships.
modelBuilder.Entity<Price>()
.HasKey(input => input.PriceId)
.HasName("PrimaryKey_Price_PriceId");
// Provide the properties of the PriceId column
modelBuilder.Entity<Price>()
.Property(input => input.PriceId)
.HasColumnName("PriceId")
.HasColumnType("int")
.UseSqlServerIdentityColumn()
.ValueGeneratedOnAdd()
.IsRequired();
I am having some trouble with EF6 lazy loading, code first to an existing database.
Here are the Entities that are giving me the issue, I have no idea why it is not working, everything I find online says it should be working.
public class User
{
public long UserId { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Token> Tokens { get; set; }
public virtual ICollection<Business> Businesses { get; set; }
public virtual ICollection<Candidate> Candidates { get; set; }
}
Here is the configuration mappings:
public class Token
{
public long TokenId { get; set; }
public long UserId { get; set; }
public Guid TokenValue { get; set; }
public DateTime ExpirationDate { get; set; }
public virtual User User { get; set; }
}
public TokenMap()
{
this.HasKey(t => t.TokenId);
this.Property(t => t.TokenValue)
.IsRequired();
this.Property(t => t.ExpirationDate)
.IsRequired();
this.ToTable("Tokens");
this.Property(t => t.TokenId).HasColumnName("TokenId");
this.Property(t => t.UserId).HasColumnName("UserId");
this.Property(t => t.TokenValue).HasColumnName("TokenValue");
this.Property(t => t.ExpirationDate).HasColumnName("ExpirationDate");
this.HasRequired(s => s.User)
.WithMany(s=>s.Tokens)
.HasForeignKey(s=>s.UserId);
}
public UserMap()
{
this.ToTable("Users");
this.HasKey(t => t.UserId);
this.Property(t => t.Email)
.IsRequired();
this.Property(t => t.FirstName)
.IsRequired();
this.Property(t => t.LastName)
.IsRequired();
this.HasMany(t => t.Businesses)
.WithMany(set => set.Users)
.Map(m =>
{
m.ToTable("BusinessUser");
m.MapLeftKey("UserId");
m.MapRightKey("BusinessId");
});
this.HasMany(s => s.Tokens)
.WithRequired(s => s.User)
.HasForeignKey(s => s.UserId);
this.HasMany(s => s.Candidates)
.WithOptional(s => s.User)
.HasForeignKey(s => s.UserId);
}
And here is a few snippets from the context:
public DbSet<Token> Token { get; set; }
public DbSet<User> User { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TokenMap());
modelBuilder.Configurations.Add(new UserMap());
}
Whenever I do a SingleOrDefault on the Tokens entity the result' user is null.
Any idea what I am doing wrong? All the data in the database is right, and UserId does have a value.
Here is a more elaborate example of calling:
I am implementing the repository patern.
In the class doing the call' constructor I have:
context = new Consolid8ContextProvider();
uow = new UnitOfWork(context);
And then uow.Tokens.First(u => u.ExpirationDate > DateTime.Now && u.TokenValue == token);
Tokens is my TokenRepository that exposes the Tokens entity, and First is a wrapper for FirstOrDefault.
This results in a token object with all of the properties set except for the User navigation property
So I was using BreezeJS and it overrides your context with it's own settings, part of which is to set LazyLoading and EnableProxiesCreation to false.
So if you want to do queries outside of breeze you either have to implement a different constructor for your breeze provider or setting it per query as Slauma has suggested in the comments of the question.
I'm trying to create a one to many relationship using entity framework to convert the json object using Web.API return.
I am creating a scenario that a User has many features emails.
However json file in the User entity that brings contains a list of Emails that appear always empty.
User Entity
[DataContract]
public class UsuarioEntity : BaseEntity
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public bool Ativado { get; set; }
[DataMember]
public ICollection<EmailEntity> Emails { get; private set; }
}
Email Entity
[DataContract]
public class EmailEntity : BaseEntity
{
[DataMember]
public string Email { get; set; }
[DataMember]
public bool Ativo { get; set; }
[DataMember]
public bool Principal { get; set; }
[DataMember]
public DateTime DataCadastro { get; set; }
[DataMember]
public UsuarioEntity Usuario { get; set; }
}
Fluent API
UsuarioConfiguration
ToTable("tbl_usuarios");
HasKey(a => new {a.Id, a.UserName});
Property(a => a.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("cd_id_usuario");
Property(a => a.UserName)
.HasMaxLength(32)
.HasColumnName("nm_usuario");
Property(a => a.Password)
.IsRequired()
.HasMaxLength(32)
.HasColumnName("tx_senha");
Property(a => a.DataCadastro)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
.HasColumnType("datetime")
.IsRequired()
.HasColumnName("dt_cadastro");
HasMany(o => o.Emails)
.WithRequired();
EmailConfiguration
ToTable("tbl_email_usuario");
HasKey(a => new { a.Id, a.Email });
Property(a => a.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("cd_id_email");
Property(a => a.Email)
.HasMaxLength(150)
.HasColumnName("nm_email");
Property(a => a.DataCadastro)
.HasColumnType("datetime")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
.IsRequired()
.HasColumnName("dt_cadastro")
I tried to use EF with Web api this way:
public IQueryable<UsuarioEntity> Get()
{
PlainContext context = new PlainContext();
return context.Usuarios;
}
Except that the return was not expected
[{"Id":1,"Ativado":true,"DataCadastro":"\/Date(1357307720730-0200)\/","Emails":[],"Password":"senha456","UserName":"recapix"}]
I own and Registered Emails only they do not appear on Json.
Your entities don't support lazy loading so you must define explicitly that you want to load related emails as well:
return context.Usarios.Include(u => u.Emails);