I have recently converted my existing database to code using EF Reverse Engineer Code First. After polishing most of the results, I've started writing tests. The error I'm currently having is trying to access a column name called 'Element_ElementCode', however no such column exists.
To be sure of this, I've performed a search on my entire project to avoid the possibility of accidentally declaring it as such.
Find all "Element_ElementCode", Subfolders, Find Results 1, Entire Solution, ""
Matching lines: 0 Matching files: 0 Total files searched: 120
The exact error is as follows:
System.Data.SqlClient.SqlException: Invalid column name 'Element_ElementCode'.
Invalid column name 'Element_ElementCode' is repeated 10-15 times and the stacktrace doesn't provide any clue.
This exception occurs when executing a test which contains an expression to retrieve data and performs some asserts onto it.
var doos = (dbContext.Elementen.Where(d => d.ElementCode == "DOOS9001")).FirstOrDefault();
This is the result from a query in SQL Server itself:
Element (inside Element.cs) has the following fields:
ElementCode
Doelgroep
Type
Omschrijving
Titel
Elements are mapped like this:
public class ElementenMap : EntityTypeConfiguration<Element> {
public ElementenMap() {
// Primary Key
this.HasKey(t => t.ElementCode);
// Properties
this.Property(t => t.ElementCode)
.IsRequired()
.HasMaxLength(255);
this.Property(t => t.Type)
.HasMaxLength(31);
this.Property(t => t.Doelgroep)
.HasMaxLength(255);
this.Property(t => t.Omschrijving)
.HasMaxLength(255);
this.Property(t => t.Titel)
.HasMaxLength(255);
// Table & Column Mappings
this.ToTable("Elementen");
this.Property(t => t.ElementCode).HasColumnName("ElementCode");
this.Property(t => t.Type).HasColumnName("Type");
this.Property(t => t.Doelgroep).HasColumnName("Doelgroep");
this.Property(t => t.Omschrijving).HasColumnName("Omschrijving");
this.Property(t => t.Titel).HasColumnName("Titel");
// Relationships
this.HasMany(t => t.Kernwoorden)
.WithRequired(t => t.Element)
.Map(m => m.ToTable("Kernwoorden"));
}
}
Furthermore: I am certain the database is accessed because other tests on different tables inside the same database succeed.
I've tried to provide as much relevant info as possible, let me know if I've forgotten a source. Why is it trying to access a column called Element_ElementCode? Is it perhaps a convention I've forgotten to turn off (I've already had to turn off the PluralizingTableNameConvention)?
What direction should I be looking for this?
Edit:
Kernwoord.cs
public class Kernwoord {
public string ElementCode { get; set; }
public string KernwoordString { get; set; }
public virtual Element Element { get; set; }
}
Element.cs
public partial class Element {
public Element() {
this.LeertrajectElementen = new List<LeertrajectElement>();
this.Kernwoorden = new List<Kernwoord>();
}
public string ElementCode { get; set; }
public string Type { get; set; }
public string Doelgroep { get; set; }
public string Omschrijving { get; set; }
public string Titel { get; set; }
public virtual Casus Casus { get; set; }
public virtual Document Document { get; set; }
public virtual Doos Doos { get; set; }
public virtual ICollection<LeertrajectElement> LeertrajectElementen { get; set; }
public virtual StellingenSpel StellingenSpel { get; set; }
public virtual ICollection<Kernwoord> Kernwoorden { get; set; }
}
LeertrajectElementenMap.cs snippet:
this.HasRequired(t => t.Element)
.WithMany(t => t.LeertrajectElementen)
.HasForeignKey(d => d.ElementCode);
EDIT:
The problem was solved by fixing the existing inheritance issues.
You don't specify the foreign key column name of your relationship. It should be:
this.HasMany(t => t.Kernwoorden)
.WithRequired(t => t.Element)
.Map(m => m.MapKey("ElementCode")); // FK column name in Kernwoorden table
Or, if you have a foreign key property ElementCode in Kernwoorden class:
this.HasMany(t => t.Kernwoorden)
.WithRequired(t => t.Element)
.HasForeignKey(t => t.ElementCode);
There is a covention with respect to types.
For example if you have a customer and that customer has one and only one Adress, and the Adress has a Postcode.
Then it will generate a table Customer with a field Adress_Postcode.
In your case your Class name and Table name are different, so it is putting the type Element into table Elementen.
Related
I've researched a ton and couldn't find a solution that works for me.
The issue I have is the following:
I have a many to many relationship among my entities and I have a join table (without a "join model" in the code). And when trying to update an entity, EF Core tries to add a row to the join table, which already exists.
The thing is, that one of the tables is constant & is populated during migration and shouldn't be ever modified. The question is, how to tell EF Core, to not add any existing, valid rows to the second table & to the join table?
Here's the code - relationship configuration:
public class FirstModelsMap : IEntityTypeConfiguration<FirstModel>
{
public void Configure(EntityTypeBuilder<FirstModel> builder)
{
builder.HasKey(p => p.Id);
builder.HasIndex(nameof(FirstModel.InternalGuid));
builder.Property(p => p.Secret)
.HasMaxLength(128)
.IsRequired();
builder.Property(p => p.Name)
.IsRequired();
builder.Property(p => p.Url)
.IsRequired();
builder.Property(p => p.InternalGuid)
.IsRequired();
builder.HasMany(p => p.SecondModels)
.WithMany(p => p.FirstModels)
.UsingEntity(p => p.ToTable("FirstModelSecondModels"));
}
}
First entity:
public class FirstModel
{
public long Id { get; set; }
public Guid InternalGuid { get; set; }
public string Name { get; set; }
public Uri Url { get; set; }
public string Secret { get; set; }
public ICollection<SecondModel> SecondModels { get; set; }
}
Second entity:
public class SecondModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string InternalName { get; set; }
public ICollection<FirstModel> FirstModels { get; set; }
}
What I try to do in the repository:
public void Update(FirstModel firstModel)
{
firstModel.SecondModels = database.SecondModels.Where(e => firstModel.SecondModels.Select(e => e.Id).Contains(e.Id)).ToList();
// Tried adding this row, based on some StackOverflow answers
database.SecondModels.AttachRange(firstModel.SecondModels);
database.Update(firstModel);
}
What I get as a result when calling SaveChanges() in the service:
Microsoft.EntityFrameworkCore.DbUpdateException: 'An error occurred while saving the entity changes. See the inner exception for details.'
Inner Exception:SqlException: Violation of PRIMARY KEY constraint 'PK_FirstModelsSecondModels'. Cannot insert duplicate key in object 'dbo.FirstModelsSecondModels'. The duplicate key value is (1, 1).
My SecondModels table is being populated with migration & is not expected to be modified. The join table may be modified. However I want to let EF Core know, that some rows may already be in place. I want the join table to adjust based on the public ICollection<SecondModel> SecondModels { get; set; } value of the FirstModel. So that not relevant rows are deleted, existing rows are not added, new rows are added.
I'm sure I'm doing something wrong here (otherwise it would work). Can you please help?
I've ended up modifying the method the following way, which made everything work as expected, although I don't think this is the best solution by any means:
public async Task<bool> UpdateAsync(FirstModel firstModel)
{
var secondModelIds = firstModel.SecondModels.Select(e => e.Id);
var existingFirstModel = await database.FirstModel .Include(w => w.SecondModels).FirstOrDefaultAsync(w => firstModel.Id == w.Id);
if (existingFirstModel is null)
{
return false;
}
existingFirstModel.SecondModels= database.SecondModels.Where(e => secondModelIds .Contains(e.Id)).ToList();
existingFirstModel.InternalGuid = firstModel.InternalGuid;
existingFirstModel.Secret = firstModel.Secret;
existingFirstModel.Url = firstModel.Url;
existingFirstModel.Name = firstModel.Name;
// This is called in the service layer
await database.SaveChangesAsync()
return true;
}
I am using Entity Framework Core 2.2 to manage a SQL Server database of traded currencies. There are two entities in the model. The first is Currency, which specifies a trade-able currency, and the other is CurrencyPair, which specifies a pair of currencies that can be exchanged for one another.
public class Currency
{
public ulong Id { get; set; }
public string Name {get; set; }
[NotMapped]
public IEnumerable<CurrencyPair> Pairs
{
get { PairsAsBase?.Concat( PairsAsQuote ?? new CurrencyPair[0] ); }
}
public virtual IEnumerable<CurrencyPair> PairsAsBase { get; set; }
public virtual IEnumerable<CurrencyPair> PairsAsQuote { get; set; }
}
public class CurrencyPair
{
public ulong Id { get; set; }
public string Name { get; set; }
public ulong BaseCurrencyId { get; set; }
public ulong QuoteCurrencyId { get; set; }
public virtual Currency BaseCurrency { get; set; }
public virtual Currency QuoteCurrency { get; set; }
}
I would like to constrain the CurrencyPair table to disallow rows from having the same Currency for both BaseCurrency and QuoteCurrency fields. That is, if a specific currency has Id = 1, then a currency pair specifying BaseCurrencyId = 1 and QuoteCurrencyId = 1 would not be allowed.
Here is my DbContext.OnModelCreating implementation:
protected override void OnModelCreating( ModelBuilder modelBuilder )
{
modelBuilder.Entity<Currency>().HasKey(x => x.Id);
modelBuilder.Entity<Currency>().HasAlternateKey(x => x.Name);
modelBuilder.Entity<Currency>()
.HasMany(x => x.PairsAsBase)
.WithOne(x => x.BaseCurrency)
.HasForeignKey(x => x.BaseCurrencyId);
modelBuilder.Entity<Currency>()
.HasMany(x => x.PairsAsQuote)
.WithOne(x => x.QuoteCurrency)
.HasForeignKey(x => x.QuoteCurrencyId);
modelBuilder.Entity<CurrencyPair>().HasKey(x => x.Id);
modelBuilder.Entity<CurrencyPair>()
.HasOne(x => x.BaseCurrency)
.WithMany(x => x.PairsAsBase)
.HasForeignKey(x => x.BaseCurrencyId);
modelBuilder.Entity<CurrencyPair>()
.HasOne(x => x.QuoteCurrency)
.WithMany(x => x.PairsAsQuote)
.HasForeignKey(x => x.QuoteCurrencyId);
}
TL;DR: How can I ensure that two foreign key columns in a table do not both reference the same entity (using Entity Framework Core 2.2)?
AFAIK there is no good way to enforce your rule at the model builder lvl.
The next best thing would be to intercept SQL commands that could generate faulty data through an ef context, but the API isnt mature enough to make this an easy option.
In my opinion, the only options that you have left have no relation whatsoever to EF:
Constrain: Enforce the rule on your DB schema, eg. through a CHECK constraint
Validate: Enforce the rule at domain model level, eg. by intercepting the setters for both properties in the class and validating their values.
Did you try Global query filters, this should help you protect some unwanted to be show up when you query
modelBuilder.Entity<CurrencyPair>().HasQueryFilter(p => p.BaseCurrency != p.QuoteCurrency);
Data will still store in your table but it not show up when you using it.
I am having problems setting up relationships in entity framework 6 and making cascade deletion work.
I have the following objects
public class Module {
public long Id { get; set; };
public virtual ICollection<Group> Groups { get; set; };
}
public class Group {
public long Id { get; set; };
public virtual ICollection<Field> Fields { get; set; };
}
public class Field {
public long Id { get; set; };
public virtual FieldLink Link { get; set; };
}
public class FieldLink {
public long Id { get; set; };
public virtual Module LinkToModule { get; set; };
}
Now a module has groups, a group has fields, a field MAY have a link. A link will have a LinkToModule, but this can be a different module then the one that the parent field/group belongs too.
I have setup my relationships like so
public ModuleConfig()
{
this.ToTable("Module");
}
public FieldGroupConfig()
{
this.ToTable("FieldGroup");
// relationships
this.HasRequired(e => e.Module)
.WithMany(e => e.Groups)
.HasForeignKey(e => e.ModuleId);
}
public FieldConfig()
{
this.ToTable("Field");
this.HasRequired(e => e.FieldGroup)
.WithMany(e => e.Fields)
.HasForeignKey(e => e.FieldGroupId);
this.HasOptional(e => e.Link)
.WithRequired(e => e.Field);
}
public FieldLinkConfig()
{
this.ToTable("FieldLink");
this.HasRequired(e => e.LinkToModule)
.WithMany()
.HasForeignKey(e => e.LinkToModuleId);
}
Now i am running my tests, and i get the following error
Test method ModuleServiceTests.ModuleService_DeleteAsync_ByEntity threw exception:
System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.FieldLink_dbo.Field_Id". The conflict occurred in database "TestDb", table "dbo.FieldLink", column 'Id'.
For if i check the relationship, it is between table Field.Id > FieldLink.Id and the DELETE rule is set as NO ACTION. Fine, so i guess i need to update that relationship and use WillCascadeOnDelete(true)
So i updated the code in FieldConfig from
this.HasOptional(e => e.Link)
.WithRequired(e => e.Field);
to
this.HasOptional(e => e.Link)
.WithRequired(e => e.Field)
.WillCascadeOnDelete(true);
But now when i try to run my test, the database isnt even created and i get the error saying
Initialization method Test.TestInitialize threw exception.
System.Data.SqlClient.SqlException: Introducing FOREIGN KEY constraint 'FK_dbo.FieldLink_dbo.Field_Id' on table 'FieldLink' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Can someone please help? Have i setup a relationship incorrectly, am i going the wrong way about this?
MS SQL Server does not supports cycles in the cascade deletes actions. You need to choose just one of the two directions to cascade deletes or find a workaround like in this answer (example of the trigger is here in Listing 6). This answer also contains some insights.
I need to map following class on two tables -
public class Centre
{
public string CentreID { get; set;}
public string Name { get; set;}
public int Order { get; set;}
public string InfoText { get; set; }
public string Description { get; set; }
}
And the mapping used is
modelBuilder.Entity<Centre>()
.Map(m =>
{
m.Properties(t => new { t.CentreID, t.Name, t.Order });
m.ToTable("Centres");
})
.Map(m =>
{
m.Property(t => t.Description).HasColumnName("InfoText");
m.ToTable("CentreContents");
m.Requires("Attribute").HasValue("Description");
})
.Map(m =>
{
//m.Properties(t => new { t.InfoText });
m.Property(t => t.InfoText);
m.ToTable("CentreContents");
m.Requires("Attribute").HasValue("Intro");
});
The first 2 map works as expected. Adding 3rd map gives error. What expected is , there is a one to many relation and each record from linked table needs to be mapped on a property. The database is already existing in the application and not possible to change the structure. With view it is possible but don't want to create view for same.
Try removing the semi colon in this part of your statement...
});
.Map(m =>
Oh and detailing the error can be pretty helpful.
I am trying to get EF Code First working with MySql 5.5.19 (Connector/Net 6.4.4) but when the database is created, it is ignoring the Map(a => a.ToTable() command for a join table.
HasMany(t => t.AllowedAssociations).WithMany()
.Map(a => a.ToTable("AllowedTemplateAssociations")
.MapLeftKey("TemplateId")
.MapRightKey("AllowedAssociationToId"));
MySql is throwing an error that there is no table called "templatetemplate" - somewhere along the line the name I have specified has been ignore and "templatetemplate" is expected. (NOTE: if I change my code to ToTable("templatetemplate") it works as expected)
My model:
public class Template
{
public Int32 Id { get; set; }
[Required]
public String Name { get; set; }
public virtual ICollection<Template> AllowedAssociations { get; set; }
}
My config:
public class TemplateConfiguration : EntityTypeConfiguration<AssetTracking.Domain.Entities.Template>
{
public TemplateConfiguration()
{
Property(t => t.Name).IsRequired();
HasMany(t => t.AllowedAssociations).WithMany()
.Map(a => a.ToTable("AllowedTemplateAssociations")
.MapLeftKey("TemplateId").MapRightKey("AllowedAssociationToId"));}
}
This works as expected if I use SQLSERVER, the table is named correctly.
Any ideas? Have I forgotten a config anywhere?
(I have modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); set)