My problem: inserting an entity with an owned property fails.
I have a Restaurant entity with an Address owned property. When I try to create an new entity and insert into the database, an exception is thrown at SaveChanges:
Cannot insert the value NULL into column 'RestaurantId', table 'AppRefDB.dbo.Addresses'; column does not allow nulls. INSERT fails.
What I did
My table Address looks like this:
CREATE TABLE [dbo].[Addresses]
(
[RestaurantId] INT NOT NULL,
[Number] NVARCHAR(8) NULL,
[Street] NVARCHAR(150) NOT NULL,
[Zip] NVARCHAR(10) NOT NULL,
[Town] NVARCHAR(50) NOT NULL,
[Site] NVARCHAR(150) NULL ,
CONSTRAINT [PK_Addresses]
PRIMARY KEY ([RestaurantId]),
CONSTRAINT [FK_Address_Restaurants_RestaurantId]
FOREIGN KEY ([RestaurantId]) REFERENCES [Restaurants] ([Id])
ON DELETE CASCADE
)
where RestaurantId is the primary key and FK from Restaurant table.
And
CREATE TABLE [dbo].[Restaurants]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
CONSTRAINT [FK_Restaurants_TCategories]
FOREIGN KEY ([IdCategory]) REFERENCES [Categories]([Id])
)
I defined my property like this in OnModelCreating:
modelBuilder.Entity<Restaurant>()
.OwnsOne(p => p.Address)
.ToTable("Addresses");
And I save like this:
await _dbContext.Set<Restaurant>()
.AddAsync(restaurant, cancellationToken);
_dbContext.SaveChangesAsync();
What I am looking for
What should I change in order to EF understand RestaurantId should get the newly created Id from Restaurant table before inserting the Address?
I am using EF Core 3.
Update works fine, I just have a problem with creating an new restaurant/address
EDIT: my model
public class Restaurant
{
[Key]
public int Id { get; set; }
[Required, StringLength(50)]
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
[Required, StringLength(150)]
public string Street { get; set; }
[StringLength(8)]
public string Number { get; set; }
[Required, StringLength(10)]
public string Zip { get; set; }
[Required, StringLength(50)]
public string Town { get; set; }
[StringLength(150)]
public string Site { get; set; }
}
Edit2 :
I tested a synchronous version as well
In this case, you have a Class object which has a collection of addresses.
using (var context = new YourContext())
{
var model= new Restaurant{ Name = "McDonald's" };
model.addresses.Add(new addresses{ street="test",.... });
model.addresses.Add(new addresses{ street="test",.... });
context.Restaurant.Add(model);
context.SaveChanges();
}
this would solve your problem.
You can add in both your classes
public ICollection<YourEntity> YourEntity{ get; set; }
or you can use foreign keys.
[ForeignKey("Restaurant")]
public long RestaurantId { get; set; }
public Restaurant Restaurant{ get; set; }
if you add this in your address entity you need to first create one resturant then add addresses separately
In fact this is probably a bug in EF 3.0
I tested with EF 3.1 (preview) and it is working fine
Related
I'm trying to make a one-to-many relationship with a composite primary key:
public class Bom
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
public string Name { get; set; }
}
public class ChildReference
{
[Key]
public int Id { get; set; }
public string BomId { get; set; } // Should be the foreign key from the bom-table
public ICollection<Bom> Boms { get; set; }
}
......
builder.Entity<ChildReference>().HasKey(t => new { t.Id, t.BomId });
When I run this, Entity Framework Core creates two columns in the Bom-table called ChildReferenceBomId and ChildReferenceId. I don't want that. I want it to only create one column caled ChildReferenceId that should be the foreign key to the ChildReference table.
The reason why I want to create a composite primary key inside the ChildReference table is because I want to add rows to the table like this:
INSERT INTO ChildReference(Id, BomId) VALUES(1, '1')
INSERT INTO ChildReference(Id, BomId) VALUES(1, '2')
I'm not sure if I'm doing this the right way. Can anyone help me?
EDIT:
I basically want to do the following with entity framework core:
CREATE TABLE [dbo].BOMChildren (
[BOMChildId] [int] NOT NULL,
[BOMId] [int] NOT NULL,
CONSTRAINT [PK_BOMChildId_BOMId] PRIMARY KEY CLUSTERED
(
[BOMChildId] ASC,
[BOMId] ASC
))
CREATE TABLE [dbo].BOM (
[BOMId] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL,
[BOMPartId] [nvarchar](64) NOT NULL,
[Qty] [int] NOT NULL,
[UnitOfMeasure] [nvarchar](32),
[ParentId] [int] NULL,
[ChildReference] [int] NULL,
[BOMItemDataId] [int]
)
ALTER TABLE [dbo].[BOMChildren]
ADD CONSTRAINT [FK_BOMChildren_BOM]
FOREIGN KEY([BOMId]) REFERENCES [dbo].[BOM] ([BOMId])
GO
ALTER TABLE dbo.Bom
ADD CONSTRAINT FK_Bom_BomChild
FOREIGN KEY(ChildReference, BOMId) REFERENCES [dbo].BOMChildren([BOMChildId], [BOMId])
Anyone who can push me in the right direction?
You can use set it up like this:
public class Bom
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
public string Name { get; set; }
public int ChildReferenceId { get; set; }
public ChildReference CurrentChildReference{ get; set; }
}
public class ChildReference
{
[Key]
public int Id { get; set; }
public ICollection<Bom> Boms { get; set; }
}
You can after that configure a one-to-many relationship for the above entities using Fluent API by overriding the OnModelCreating method in the context class, as shown here:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// configures one-to-many relationship
modelBuilder.Entity<Bom>()
.HasRequired<ChildReference>(s => s.CurrentChildReference)
.WithMany(g => g.Boms)
.HasForeignKey<int>(s => s.ChildReferenceId);
}
You can use the Create/Alter SQL statements that you posted above to create the tables within your database.
After that you can do a Reverse Engineer Model to generate EF code first DbContext based on your existing Database tables. You can follow the steps within chapter 3 (https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/workflows/existing-database)
So i wanted to apply a relation of 1 to 1 from one table to another, with navigational properties on each one and a foreign key that is accessable on at least one of the models.
Lets suppose this example
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int ContactId { get; set; }
public virtual Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
modelBuilder.Entity<User>().HasOptional<Contact>(u=> u.Contact)
.WithRequired(c => c.User).Map(m => m.MapKey("ContactId")).
Similar to the same example used in this stack overflow question:
EF Code First - 1-to-1 Optional Relationship
The problem is that it gives an error saying that the Property name 'ContactId' is already defined.
But i want to have this foreign property defined both at the database and on the model, so that i can use for example linq:
this.dbContextProvider.CurrentContext.User.SingleOrDefault(src => src.ContactId == contactId);
or is this acceptable or very inneficient:
this.dbContextProvider.CurrentContext.User.SingleOrDefault(src => src.Contact.Id == contactId);
This last options will create a join between the two tables while query the database, right?
The downside of the correct model (i.e. without explicit User.ContactId property) is that in reality it's still a 1:n relationship. The database doesn't enforce 1:1. It's just a FK. The only way to make a true, database-enforced 1:1 association in EF6 is one in which the dependent entity (here: User) has a primary key that's also a foreign key to the principal entity (Contact):
public class User
{
public int Id { get; set; }
public string Username { get; set; }
//public int ContactId { get; set; } <= removed
public virtual Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
And:
modelBuilder.Entity<User>()
.HasRequired<Contact>(u => u.Contact)
.WithOptional(c => c.User);
This generates the following database schema:
CREATE TABLE [dbo].[Users] (
[Id] [int] NOT NULL,
[Username] [nvarchar](max),
CONSTRAINT [PK_dbo.Users] PRIMARY KEY ([Id])
)
CREATE TABLE [dbo].[Contacts] (
[ID] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Contacts] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_Id] ON [dbo].[Users]([Id])
ALTER TABLE [dbo].[Users] ADD CONSTRAINT [FK_dbo.Users_dbo.Contacts_Id]
FOREIGN KEY ([Id]) REFERENCES [dbo].[Contacts] ([ID])
As for querying, in a query like context.Users.Where(u => u.Contact.ID == 4), EF6 will notice that no Contact fields are requested and it will short-circuit the FK to User.Id, i.e. no join. But of course, in this setup, you may as well use context.Users.Where(u => u.Id == 4).
In EF core it would be possible to use your model, with User.ContactId, by this mapping:
modelBuilder.Entity<User>()
.HasOne(u => u.Contact)
.WithOne(c => c.User)
.HasForeignKey<User>(u => u.ContactId);
EF core is smart enough to create a unique index on User.ContactId, so this is a database-enforced 1:1 association with a separate FK.
I have two tables:
CREATE TABLE [dbo].[FormGridElementColumns]
(
[FormGridElementColumnId] [uniqueidentifier] NOT NULL,
[FormGridElementSchemaId] [uniqueidentifier] NOT NULL,
[Caption] [nvarchar](256) NOT NULL,
[Ordering] [int] NOT NULL,
[Precision] [int] NULL
)
ALTER TABLE [dbo].[FormGridElementColumns]
ADD CONSTRAINT [PK_FormGridElementColumns]
PRIMARY KEY CLUSTERED ([FormGridElementSchemaId] ASC,
[FormGridElementColumnId] ASC)
and
CREATE TABLE [dbo].[FormGridElementColumnDataAdapters]
(
[FormGridElementColumnDataAdapterId] [uniqueidentifier] NOT NULL,
[FormGridElementColumnId] [uniqueidentifier] NULL,
[FormGridElementSchemaId] [uniqueidentifier] NULL,
[DisplayColumn] [nvarchar](256) NOT NULL
)
ALTER TABLE [dbo].[FormGridElementColumnDataAdapters]
ADD CONSTRAINT[PK_FormGridElementColumnDataAdapters]
PRIMARY KEY CLUSTERED ([FormGridElementColumnDataAdapterId] ASC)
GO
ALTER TABLE [dbo].[FormGridElementColumnDataAdapters] WITH CHECK
ADD CONSTRAINT [FK_FormGridElementColumnDataAdapters_FormGridElementColumns]
FOREIGN KEY ([FormGridElementSchemaId], [FormGridElementColumnId])
REFERENCES [dbo].[FormGridElementColumns]([FormGridElementSchemaId], [FormGridElementColumnId])
ON DELETE CASCADE
And models:
public class FormGridElementColumn
{
public Guid FormGridElementColumnId { get; set; }
public Guid FormGridElementSchemaId { get; set; }
public string Caption { get; set; }
public int Ordering { get; set; }
public int? Precision { get; set; }
public FormGridElementColumnDataAdapter FormElementDataAdapter { get; set; }
}
public class FormGridElementColumnDataAdapter
{
public Guid FormGridElementColumnDataAdapterId { get; set; }
public Guid? FormGridElementSchemaId { get; set; }
public Guid? FormGridElementColumnId { get; set; }
public string DisplayColumn { get; set; }
}
Everything worked well before the update. But after moving to .NET Core 3.1, I get the following error:
System.InvalidOperationException: The child/dependent side could not be determined for the one-to-one relationship between 'FormGridElementColumnDataAdapter' and 'FormGridElementColumn.FormElementDataAdapter'. To identify the child/dependent side of the relationship, configure the foreign key property. If these navigations should not be part of the same relationship configure them without specifying the inverse.
Trying to fix this in OnModelCreating:
modelBuilder.Entity<FormGridElementColumn>(b =>
{
b.HasKey(p=> new {p.FormGridElementSchemaId, p.FormGridElementColumnId});
...
b.HasOne(c => c.FormElementDataAdapter).WithOne().HasForeignKey<FormGridElementColumnDataAdapter>(k=> new{ k.FormGridElementSchemaId, k.FormGridElementColumnId});
...
b.ToTable("FormGridElementColumns");
});
But now I get other errors:
Invalid column name 'FormGridElementColumnFormGridElementSchemaId'.
Invalid column name 'FormGridElementColumnId1'.
Invalid column name 'FormGridElementColumnFormGridElementSchemaId'.
Invalid column name 'FormGridElementColumnId1'.
Invalid column name 'FormGridElementColumnFormGridElementSchemaId'.
Invalid column name 'FormGridElementColumnId1'.
Invalid column name 'FormGridElementColumnFormGridElementSchemaId'.
Invalid column name 'FormGridElementColumnId1'.
Invalid column name 'FormGridElementColumnFormGridElementSchemaId'.
Invalid column name 'FormGridElementColumnId1'.
Invalid column name 'FormGridElementColumnFormGridElementSchemaId'.
Invalid column name 'FormGridElementColumnId1'.
Same error if I'm trying to add navigation property column into child entity (FormGridElementColumnDataAdapter).
Any help will be appreciated!
UPD1:
I this possible that EF Core 3.1 has some changes related to keys types mapping? I have primary key as pair of non nullable Guids but foreign key is pair of nullable ones..not sure that this is the case but just trying to find solution.
You need to use navigation properties in your classes when you have a foreign key :
public class FormGridElementColumnDataAdapter
{
public Guid FormGridElementColumnDataAdapterId { get; set; }
//Using the object instead of the key
public FormGridElementColumn FormGridElementColumn { get; set; }
public string DisplayColumn { get; set; }
}
And in your model builder :
modelBuilder.Entity<FormGridElementColumn>(b =>
{
b.HasOne(c => c.FormElementDataAdapter).WithOne(x => x.FormGridElementColumn).HasForeignKey<FormGridElementColumnDataAdapter>(k => new{ k.FormGridElementSchemaId, k.FormGridElementColumnId});
});
I'm using Entity Framework 6 and sqlite to store and retrieve some data. When the following code block runs, an exception is thrown on db.SaveChanges().
using (var db = new RepoDatabase(DBPath))
{
var folder1 = new Folder
{
Name = "name1",
Subtitle = "subtitle",
ParentFolder = null,
IsHidden = false,
IsSimple = true,
QueryOrFilePath = "query"
};
var folder2 = new Folder
{
Name = "name2",
Subtitle = "subtitle",
ParentFolder = folder1,
IsHidden = false,
IsSimple = true,
QueryOrFilePath = "query"
};
db.Folders.Add(folder1);
db.Folders.Add(folder2);
db.SaveChanges();
}
Exception:
'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll
Additional information: Unable to determine the principal end of the 'RhythmRepository.Folder_ParentFolder' relationship. Multiple added entities may have the same primary key.
From my understanding, this problem often occurs when the ID is used directly for foreign keys, but that doesn't seem to be the case here, as the type of "ParentFolder" is "Folder".
The folder type is set to auto-increment in the database, and has the attribute:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
The underlying entity structure:
class Folder
{
#region database fields
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FolderID { get; set; }
public string Name { get; set; }
public string Subtitle { get; set; }
[ForeignKey("FolderID")]
public Folder ParentFolder { get; set; }
public bool IsHidden { get; set; }
public bool IsSimple { get; set; }
public string QueryOrFilePath { get; set; }
#endregion
}
And the SQL query to create the table:
CREATE TABLE IF NOT EXISTS Folders
(
FolderID INTEGER PRIMARY KEY AUTOINCREMENT,
Name varchar(255) NOT NULL,
Subtitle varchar(255) NULL,
ParentFolderID INT NULL,
IsHidden BIT NOT NULL,
IsSimple BIT NOT NULL,
QueryOrFilePath varchar(255) NOT NULL,
FOREIGN KEY (ParentFolderID) REFERENCES Folders(FolderID)
);
The error is in the part
[ForeignKey("FolderID")]
public Folder ParentFolder { get; set; }
This makes EF think that FolderID is the foreign key to the parent Folder. In reality, it's ParentFolderID. So change your class definition and mapping to
class Folder
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FolderID { get; set; }
public int? ParentFolderID { get; set; }
[ForeignKey("ParentFolderID")]
public Folder ParentFolder { get; set; }
...
}
If FolderID is the foreign key, EF concludes that there is a 1:1 association between a folder and its parent. Normally, a 1:1 association is implemented by a primary key that's also the foreign key to the parent. I.e the child's primary key copies its parent's primary key. When parent and child are the same entity class, two records of the same table would have to have the same primary key --impossible.
I have two table Subscription and PackageType. Subs has FK as PackageTypeId. Now when I am inserting a new record in Subscription table using EF 4.1 it is throwing an exception
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Subscription_PaymentType". The conflict occurred in database "MyDatabaseName", table "dbo.PaymentType", column 'Id'.
The statement has been terminated.
Tables information are:
Subscription Table:
Id (PK)
PaymentTypeId (FK)
Period
date
PaymentType:
Id (PK)
Name
And the Code is as given below:
public void Proceed(SubscriptionSessionData data)
{
if (data != null)
{
PMSCatalogEntities entities = new PMSCatalogEntities();
Subscription subs = new Subscription();
subs.Id = Guid.NewGuid();
subs.ApplicableFrom = data.ApplicableFrom;
subs.TenantId = tenant.Id;
subs.PackageId = data.PaymentType;
subs.PaymentTypeId = data.PaymentType;
entities.AddToSubscriptions(subs);
entities.SaveChanges();
}
}
Any idea about this issue?
I have already tested this scenario and it works great:
class Program
{
static void Main(string[] args)
{
PMSCatalogEntities entities = new PMSCatalogEntities();
Subscription subs = new Subscription();
subs.Id = Guid.NewGuid();
subs.Date = DateTime.Now;
subs.PaymentId = 1;
entities.Subscriptions.Add(subs);
entities.SaveChanges();
}
}
public class PMSCatalogEntities : DbContext
{
public DbSet<Subscription> Subscriptions { get; set; }
public DbSet<Payment> Payments { get; set; }
}
public class Subscription
{
public Guid Id { get; set; }
public DateTime Date { get; set; }
public Payment Payment { get; set; }
public int PaymentId { get; set; }
}
public class Payment
{
public int Id { get; set; }
public string Type { get; set; }
}
And on the DB
TABLE [dbo].[Payments](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Type] [nvarchar](max) NULL,
TABLE [dbo].[Subscriptions](
[Id] [uniqueidentifier] NOT NULL,
[Date] [datetime] NOT NULL,
[PaymentId] [int] NOT NULL,
The only thing I have to be sure is that a Payment with Id 1 for this example is persisted on the Payments table, and it works like a charm!!
I saw this recently when the relationship used the primary keys of the parent and child record together, instead of the primary key and foreign key. There was a clue on the diagram for this in that the cardinality was wrong (1->0.1 instead of 1 -> *)
Are you sure that the value that you are assigning to subs.PaymentTypeId (data.PaymentType) is a valid Id from an existing record on the PaymentType table?
You are trying to insert a bad reference table primary key value, which is not presented in the database.