I have a problem with populating the right values in a junction table for a many to many relationship. In the image below I have simplified what I am trying to do. The "Table on the left" has values in the database that I want to use. The table of the right is about to receive new records. It has a navigation property to the Junction table and the same is true for the table on the left. The junction table has navigation properties to both tables on its rear and they are set to required.
When I create the new records in the table on the right, I also add to it records in the junction table. The TOL_ID is known as it is saved in the database, but the TOR_ID is about to be created, therefore unknown. When I attempt to call the SaveChanges in my context, it tries to save the junction table records first, before the TOR_IDs have been populated for the record on the right. I though that marking the navigation property as Required would make the EF understand that TOR_ID must exist before creating the junction table row. Instead it tries to insert an existing TOL_ID and 0, which gives a violation when trying to insert many table on the right records connected to the same TOL_ID.
Note: Saving the TOR_IDs first and then connecting with junction records is not an option, as the creation of the junction table records are part of a "Slowly changing dimension" type 6 flow.
This is how it really looks like in the code:
// The newRating is the new object corresponding the Table on the right
var newRating = new ModuleRating()
{
// The moduleRating.RatedDriveUnit already exists in the db
RatedDriveUnit = moduleRating.RatedDriveUnit
};
newModule.Ratings.Add(newRating);
If you follow the Code First below classes will helpful
public class TOL
{
[Key]
public int TOL_ID { get; set; }
public int Col1 { get; set; }
public ICollection<TOR> Tors { get; set; }
}
public class TOR
{
[Key]
public int TOR_ID { get; set; }
public int Col1 { get; set; }
public ICollection<TOL> Tols { get; set; }
}
public class TolTorContext : DbContext
{
public DbSet<TOL> Tols { get; set; }
public DbSet<TOR> Tors { get; set; }
}
If you follow database first approach,make FK at Join Table and try to chhange id names TOLId , TORId
I'am sorry for spending your time. After some investigation I noticed that the supposed composite unique index (TOL_ID and TOR_ID) of the junction table had been set wrong. Instead two separate unique indexes had been applied to TOL_ID and TOR_ID, which resulted a constrain violation as soon as one of those two values appeared twice.
Related
I am making a web app, and one of the db tables holds client information and design settings for the site:
public class Client
{
public string Name { get; set; }
public string SiteName { get; set; }
public string PrivacyStatement { get; set; }
public bool HasLogo { get; set; }
// some more properties
}
This table will never have more than one record, and it does not have any related tables. Is it ok to omit the PK in this case?
Will the record will always be static ?
If Yes, it is absolutely fine to omit the Primary key if you are 100% sure that this table will have only one record and also do not have any relation with other tables.(Why do you even want to store it in table? Think a bit)
If NO, then also you don't need to store it in a table simply store it in a object and change it accordingly.
There is also a nice discussion on this post about primary key you can look into it.
Should each and every table have a primary key?
Background Information
I am currently working with EF Core using a database first implementation.
Current tables
Fizz
{
[Id] INT
[Category] varchar
[Value] varchar
}
Buzz
{
[Id] UniqueIdentifier
[TypeId1] INT
[TypeId2] INT
CONSTRAINT [FK_Buzz_Fizz_1] FOREIGN KEY ([TypeId1] REFERENCES [Fizz][Id])
CONSTRAINT [FK_Buzz_Fizz_2] FOREIGN KEY ([TypeId2] REFERENCES [Fizz][Id])
}
Fizz currently acts a lookup table. Doing this allows for a single data repository to be used to find different values by category.
Buzz is a table that has two different type values to be stored e.g. TypeId1 could be brand which would exist in Fizz as (id, Brands, Nestle) and TypeId2 could be a flavor which would exist in Fizz as (id, Flavors, Grape).
The Issue
I scaffold the db to create the Data Models.
When running the application the following occurrs:
InvalidOperationException: Unable to determine the relationship represented by navigation property 'Buzz.TypeId1' of type 'Fizz'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
One solution that has occurred to me is to break this lookup table (Fizz) into multiple tables that way the references could be resolved by not having duplicate types used for Foreign Keys.
This would require re-work of the logic for the current data repository to either access multiple tables or be split into multiple data repos.
Another solution would be to modify the DBContext that is generated and use DataAnnotations on the DataModel. I would like to avoid doing this as the Context and Models will be regenerated in the future and these changes will be overwritten.
Is there a way to have a datamodel generated from a table that has multiple Foreign Keys to a single table without having to modify the generated code?
For posterity:
With the database approach a scaffold of the db is done to create the context and data models.
The data models generated (using the example tables above) look something like this -
public partial class Buzz
{
public Buzz()
{ }
public Guid Id { get; set; }
public int TypeId1 { get; set; }
public int TypeId2 { get; set; }
public Fizz TypeId1Fizz { get; set; }
public Fizz TypeId2Fizz { get; set; }
}
public partial class Fizz
{
public Fizz()
{ }
public int Id { get; set; }
public string Category { get; set; }
public string Value { get; set; }
public ICollection<Buzz> TypeId1Fizz { get; set; }
public ICollection<Buzz> TypeId2Fizz { get; set; }
}
The issue is that the relationship in Buzz could not be resolved.
The solution
When using scaffold on the database all models are generated as partials to a specified folder. I created a partial for the Buzz class in another directory that lives inside of the directory created by the scaffold (be sure that the namespaces match VS likes to add the directory name to the namespace and the partials won't be matched).
public partial class Buzz
{
[NotMapped]
public Fizz TypeId1Fizz { get; set; }
[NotMapped]
public Fizz TypeId2Fizz { get; set; }
}
but Leustherin then you lose the ability to utilize .Include for Fizz! EntityFramework won't create an SQL join statement for you so you will have to make an extra trip to the DB to obtain your look up value!
To get around this, override the Get or GetAll function of your data repository and create your own join statement.
Why I chose this solution
Maintainability.
Anytime the DataModels are regenerated instead of getting a runtime error there is now a compile error reminding the dev to delete the extra properties from the generated data model.
There is no other modification of automatically generated files.
There are no major schema changes done to accommodate the change.
I will do my best to keep this updated.
Currently for EF5, but planning to upgrade to EF6 'soon'.
With following Tables:
Note these tables are simplified to explain the issue, the real tables are obviously more complex. The database is a legacy database and I can't change the tables.
BarRed and BarBlue are different tables with a different structure, only the way they link to Foo is identical.
[Table("Foo")]
public class Foo
{
// Id+ColorCode is the primary Key on this table
[Key,Column(Order=0)]
int Id;
[Key,Column(Order=1)]
int ColorCode; // 1=red, 2=blue, 3=green, …
public virtual ICollection<BarRed> BarRed { get; set; } // when ColorCode==1, empty/null otherwise
public virtual ICollection<BarRed> BarBlue { get; set; } // when ColorCode==2, empty/null otherwise
}
[Table("BarRed")]
public class BarRed
{
[Key]
int Id; // Id is the primary key in this table.
// There is no ColorCode column in this table, essentially the ColorCode for each record in this table is the literal value 1.
[ForeignKey("Id,1")] // This doesn't work, doesn't accept the 1
public virtual Foo Foo { get; set; }
}
[Table("BarBlue")]
public class BarBlue
{
[Key]
int Id; // Id is the primary key in this table.
// There is no ColorCode column in this table, essentially the ColorCode for each record in this table is the literal value 2.
[ForeignKey("Id,2")] // This doesn't work, doesn't accept the 2
public virtual Foo Foo { get; set; }
}
So in short, how do I get EntityFramework to link 2 tables in a One-To-Many relation when the 'Many' side table doesn't have an explicit column for one of the key segments, but a fixed literal value should be used on all rows.
I also have a similar issue/problem in the other direction, a One-To-Many where the 'One' side doesn't have an explicit column on each record but a literal value should be used.
I can join the tables from SQL and this does work and uses the indexes properly, I need this for EF to make the database accessible from OData.
I have a database with a lot of tables created using code-first.
3 of the tables are
public class Machine
{
[Key]
public long ID { get; set; }
...
public virtual MachineTypeApprovalHist MachineTypeApproval { get; set; }
}
public class MachineTypeApprovalHist
{
[Key]
public long ID { get; internal set; }
...
}
public class MachineTypeApproval
{
[Key]
public long ID { get; set; }
...
}
The weird thing is that EF creates a foreign key from Machine to MachineTypeApproval (not MachineTypeApprovalHist as it should!).
I found out after long time of debugging by looking in the database table directly to see the relations between the tables. The error I got was
The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_dbo.Machines_dbo.MachineTypeApprovals_MachineTypeApproval_ID". The conflict occurred in database "ATPData", table "dbo.MachineTypeApprovals", column 'ID'.
The statement has been terminated.
The error comes because it tries to use a ID from MachineTypeApprovalHist that is not in MachineTypeApprovals.
I have tried to rename the property MachineTypeApproval to TypeApproval and making a new migration, but it only renamed the table column and index.
I cannot recreate the database from scratch since I will lose my data, so what can I do to fix this?
public class DatabaseContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<Machine>()
.HasOptional(_ => _.MachineTypeApproval)
.WithMany();
}
}
will generate these SQL statements:
ALTER TABLE [dbo].[Machines] WITH CHECK ADD CONSTRAINT [FK_dbo.Machines_dbo.MachineTypeApprovalHists_MachineTypeApproval_ID] FOREIGN KEY([MachineTypeApproval_ID])
REFERENCES [dbo].[MachineTypeApprovalHists] ([ID])
GO
ALTER TABLE [dbo].[Machines] CHECK CONSTRAINT [FK_dbo.Machines_dbo.MachineTypeApprovalHists_MachineTypeApproval_ID]
GO
I found a way to fix this, though I feel it should be easier.
The way I did it was to add another property to my Machine class to replace the first one.
public class Machine
{
[Key]
public long ID { get; set; }
...
public virtual MachineTypeApprovalHist MachineTypeApproval { get; set; }
//new property
public virtual MachineTypeApprovalHist TypeApproval {get; set;}
}
then I made a new migration which creates a new columns with foreign key to the correct table.
Now, I couldn't just remove my original property and update the database since I would lose data. So first i removed the original property MachineTypeApproval from Machine class, then adding a new migration, adding to that migrations Up method the following on the first line before any other call
Sql("update [dbo].Machines set TypeApproval_ID = MachineTypeApproval_ID ");
In this way the first migration correctly creates the new property, the second migration copies data from the old column to the new, and the second migration removes the old column, and my model and db is now correct.
I just hate that I need two migrations to do this. Also it looks like EF uses the property names BEFORE the property type to determine which table to use, which seems totally crazy to me
i have a project here, were a big amount of data is read from different sources. In a special logic, a data/object-modell is build with these data. So as a result i retrieve a complete SQLite capable object model.
The data were previously written to the SQLite database using a simple:
_connection.InsertWithChildren(model, true);
But, since the source of the data became bigger, this is not possible anymore, cause the Insert method will throw an "too many variables" exception. ;(
Now, i am looking for an replacement for this method. The difficulty here is that within my model, i nearly always have Foreign-Keys in both directions. Parent has Childs, Childs knows Parent.
Performance is not an issue. I don't care if the function needs 10Seconds or 5Minutes. But does anyone have an idea how to handle the Insert, while all Foreign Keys are filled correctly?
If i use a simple
foreach(var entity in _entityList)
_connection.Insert(entity);
the foreign Keys (IDs) are all Guid.Empty;
best regards and cheers,
Chris
Until issue #64 is fixed you can use ReadOnly properties on lists.
For example:
public class Foo
{
[PrimaryKey]
public Guid Id { get; set; }
[OneToMany(ReadOnly = true)]
public List<Bar> Bars { get; set; }
}
public class Bar
{
[PrimaryKey]
public Guid Id { get; set; }
[ForeignKey(typeof(Foo))]
public Guid ParentId { get; set; }
[ManyToOne]
public Foo ParentFoo { get; set; }
}
Will no longer hit the variable limit issue regardless of the operation executed.
You can now insert the elements safely:
// Insert parent 'foo' element
// This won't insert the children or update their foreign keys
conn.InsertWithChildren(foo);
// Insert all children
// This will also update ParentId foreign key if ParentFoo property is set
conn.InsertAllWithChildren(bars)
Or use plain SQLite.Net methods assigning the foreign keys yourself:
conn.Insert(foo);
foreach (var bar in bars) {
bar.ParentId = foo.Id;
conn.Insert(bar);
}