I am working with Entity Framework 5 code-first and I have a situation where I have an entity which has an Identity column that is not part of the primary key. When I add a new record and invoke SaveChanges on the context, I get a ConcurrencyException. If I change the primary key on the entity map to SCHED_ID or remove SCHED_ID from the entity map altogether, SaveChanges completes without issue. I suspect that since SCHED_ID is defined as an int, it defaults to a value of zero and Entity Framework thinks it has changed when the database actually assigns it a value (thus causing a ConcurrencyException). Am I correct in my assumption? How can I work around this without changing the key on the entity map to SCHED_ID?
The entity is defined as follows:
public partial class Sched
{
public int SCHED_ID { get; set; }
public System.DateTime DATE_QUEUED { get; set; }
public string STATUS_CODE { get; set; }
}
public class SchedMap : EntityTypeConfiguration<Sched>
{
public SchedMap()
{
// Primary Key
this.HasKey(t => new { t.STATUS_CODE, t.DATE_QUEUED });
//Properties
this.Property(t => t.SCHED_ID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsConcurrencyToken(false);
// Table & Column Mappings
this.ToTable("Sched");
this.Property(t => t.SCHED_ID).HasColumnName("SCHED_ID");
this.Property(t => t.STATUS_CODE).HasColumnName("STATUS_CODE");
this.Property(t => t.DATE_QUEUED).HasColumnName("DATE_QUEUED");
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SchedMap());
}
Related
I need to create a unique constraint on multiple fields, and those fields are ValueObjects.
Let's say I have this
public class MyEntity
{
public EntityCode Code {get;set;}
public SecondaryCode Second {get;set;}
}
public class EntityCode : ValueObject<string>
{
public string Value {get;set;}
public string Description {get;set;}
}
public class SecondaryCode: ValueObject<string>
{
public string Value {get;set;}
public string Description {get;set;}
}
I can create a unique constraint like this
public class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
{
...
builder.OwnsOne(p => p.Code)
.HasIndex(p => p.Value)
.IsUnique()
...
}
But I would like a composite key and I can't figure out how to do this with value object.
Take a look at this sample, it is not working as I expected
builder.OwnsOne(p => p.Code);
builder.OwnsOne(p => p.Second);
builder
.HasIndex(p => new { p.Code.Value, p.Seconde.Value })
.IsUnique()
Any help would be appreciated
A key serves as a unique identifier for each entity instance. Most entities in EF have a single key, which maps to the concept of a primary key in relational databases. You can also configure multiple properties to be the key of an entity - this is known as a composite key.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
builder.Entity<MyEntity>()
.HasKey(p => new { p.Code.Value, p.Seconde.Value });
}
Composite keys can only be configured using the Fluent API, conventions will never set up a composite key, and you can not use Data Annotations to configure one.
To create this composite primary key with these two columns, override DbContext.OnModelCreating(). This method is called when the model for a derived context has been initialized, but before the model has been locked down and used to initialize the context.
public class MyDbContext : DbContext
{
// Normal DbContext stuff here
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
builder.Entity<MyEntity>()
.HasKey(p => new { p.Code.Value, p.Seconde.AnotherValue });
}
}
after that generate the migration and update database.
I have a typical master/detail (User / Settings table) table schema (SQL Server) and setup Entity Framework using Fluent API to work with those tables.
I define this as an independent association, so the UserProfileSetting class doesn't include the UserId property, but I understand is correctly mapped in the configuration.
Well, my problem is that when one item of Settings is updated for a profile, at the database level that settings is updated for all users. Basically USER_ID is not considered.
The SQL query produced is this:
UPDATE [dbo].[T_USERPROFILE_SETTING]
SET [VALUE] = #0
WHERE ([KEY] = #1)
Any idea what could be wrong? I guess that if I finally add the UserId property to UserProfileSettings, that will fix the problem, but I wanted to try to fix this without it.
Current code below...
Code updating the data
var entry = profile.Settings.Where(s => s.Key == key).SingleOrDefault();
if (entry != null)
{
entry.Value = value;
} else {
var setting = /* Here create a new setting */
profile.Settings.Add(setting);
}
DataContext.SaveChanges();
Entities:
public partial class UserProfile
{
[Key]
public string UserId { get; set; }
public DateTimeOffset LastLogin { get; set; }
public ICollection<UserProfileSetting> Settings { get; set; }
}
public class UserProfileSetting
{
public UserProfileSetting() { }
public string Key { get; set; }
public string Value { get; set; }
}
Entity configuration:
public class UserProfileConfiguration : EntityTypeConfiguration<UserProfile>
{
public UserProfileConfiguration()
{
ToTable("T_USERPROFILE");
HasKey<string>(p => p.UserId);
Property(p => p.UserId)
.HasColumnName("USER_ID")
.HasMaxLength(50)
.IsUnicode()
.IsRequired();
Property(p => p.LastLogin)
.HasColumnName("LAST_LOGIN_AT")
.IsRequired();
HasMany<UserProfileSetting>(p => p.Settings)
.WithOptional()
.Map(m => m.MapKey("USER_ID"));
}
}
public class UserProfileSettingConfiguration : EntityTypeConfiguration<UserProfileSetting>
{
public UserProfileSettingConfiguration()
{
ToTable("T_USERPROFILE_SETTING");
HasKey(p => p.Key );
Property(p => p.Key)
.HasColumnName("KEY")
.HasMaxLength(50)
.IsUnicode()
.IsRequired();
Property(p => p.Value)
.HasColumnName("VALUE")
.IsUnicode()
.IsRequired();
}
}
From EF documentation...
When foreign key columns are not included in the model, the association information is managed as an independent object. Relationships are tracked through object references instead of foreign key properties. This type of association is called an independent association. The most common way to modify an independent association is to modify the navigation properties that are generated for each entity that participates in the association.
So, I was wrong. In my code, UserProfile should include UserProfileSetting either as a FK (Just the ID) or as an independent Object.
In the 1st case a UserId should be mapped into UserProfileSetting and the navigation property in UserProfile should be changed to...
HasMany<UserProfileSetting>(p => p.Settings)
.WithOptional()
.HasForeignKey(s => s.UserId);
In the 2nd case, (this is what is called an Independent Association) a new navigation property should be added into UserProfileSetting for UserProfile.
Entity framework maps to relational database and so it must stick with some of it concepts. The main thing here is, that each entity is mapped to a table containing all the records of that entity and it needs some data to distinguish the relation.
Therefore you need to add USER_ID to tell which record is for which user (to define the relation). In other words you need to have it in table and also in C# entity.
I don’t think it is possible in code first to not have the relation property on entity. On the other hand, you can create some extra DTO layer to hide it.
I'm using .NET Core end Entity Framework core to build many-to-many relation between two entities. I've built join entity to fullfil the relation and based primary key on shadow properties like this :
Entity User :
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
public List<UserGroup> UsersGroups { get; set; }
}
Entity Group :
public class Group
{
[Key]
public int IDGroup { get; set; }
[Required]
public string GroupName { get; set; }
public List<UserGroup> UsersGroups { get; set; }
}
Entity UserGroup :
public class UserGroup
{
public Group Group { get; set; }
public User User { get; set; }
}
DBcontext class :
public class DBContext : DbContext
{
public DBContext(DbContextOptions<DBContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// shadow property - primary/foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDUser");
// shadow property - primary/foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDGroup");
// composite primary key based on shadow properties
modelBuilder.Entity<UserGroup>()
.HasKey( new string[]{ "IDUser", "IDGroup" });
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.Group)
.WithMany(g => g.UsersGroups)
.HasForeignKey(???); //what to do here ?
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.User)
.WithMany(u => u.UsersGroups)
.HasForeignKey(???); // what to do here ?
base.OnModelCreating(modelBuilder);
}
public DbSet<Group> Groups { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
}
Now. How can I properly establish Foreign Key on UserGroup entity based on my shadow composite primary key ? I would like this shadow primary key to be foreign key simultaneously. I don't know how to refer to this shadow primary key now in order to make foreign key. I marked where I don't know what to do with question markes.
.HasForeignKey() declares Foreign Key Properties on your entity.
If you don't want Foreign Key Properties on your linking Entity (and you should have them), just omit the .HasForeignKey declaration and EF will use map the FK columns by convention.
eg
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// shadow property - primary/foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDUser");
// shadow property - primary/foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDGroup");
// composite primary key based on shadow properties
modelBuilder.Entity<UserGroup>()
.HasKey(new string[] { "IDUser", "IDGroup" });
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.Group)
.WithMany(g => g.UsersGroups);
//.HasForeignKey(???); //what to do here ?
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.User)
.WithMany(u => u.UsersGroups);
//.HasForeignKey(???); // what to do here ?
base.OnModelCreating(modelBuilder);
}
Generates
CREATE TABLE [UserGroups] (
[IDUser] int NOT NULL,
[IDGroup] int NOT NULL,
CONSTRAINT [PK_UserGroups] PRIMARY KEY ([IDUser], [IDGroup]),
CONSTRAINT [FK_UserGroups_Groups_IDGroup] FOREIGN KEY ([IDGroup]) REFERENCES [Groups] ([IDGroup]) ON DELETE CASCADE,
CONSTRAINT [FK_UserGroups_Users_IDUser] FOREIGN KEY ([IDUser]) REFERENCES [Users] ([IDUser]) ON DELETE CASCADE
);
You are trying to create a many-to-many join table without defining any scalar properties and you are utilizing shadow property to configure the join table. For EF fluent API, where you have to refer to a shadow property you need to use string based methods. Due to lack of backing CLR property lambda expression doesn't work.
In your case, "what to do here" part is just use the string name of the property.
e.g.
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.Group)
.WithMany(g => g.UsersGroups)
.HasForeignKey("IDGroup"); //what to do here ?
Same for the other relationship. This is general mechanism when you want to configure a shadow property as your foreign key property. Furthermore, configuring the shadow property in HasForeignKey does not require you to define the shadow property in advance, since EF can infer the type based on the properties on principal side of relationship. Though for HasKey you still need to declare the shadow properties since EF has no knowledge of types. (As you have done in your example)
EF Core also has convention to figure out FK property. One of the convention is to use property as FK if it has same name as the principal property. In your special case as above, since your principal side primary key property is named as IDGroup which is same as foreign key you are trying to configure, EF will use that by convention automatically. That means you can ignore configuring your relationship (as #David suggested). Also since EF discovers relationships based on navigations you can remove following piece of code fully from your application and it will create the same model.
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.Group)
.WithMany(g => g.UsersGroups);
//.HasForeignKey(???); //what to do here ?
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.User)
.WithMany(u => u.UsersGroups);
//.HasForeignKey(???); // what to do here ?
public class Station : IEntitie
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<RegulatorySchedule> RegulatoryScheduleDispatchStations { get; set; }
public virtual ICollection<RegulatorySchedule> RegulatoryScheduleDestinationStations { get; set; }
}
public class RegulatorySchedule : IEntitie
{
[Key]
public int Id { get; set; }
public virtual Station DispatchStation { get; set; }
public virtual Station DestinationStation { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RegulatorySchedule>()
.HasOne(s => s.DestinationStation)
.WithMany(s => s.RegulatoryScheduleDestinationStations)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
modelBuilder.Entity<RegulatorySchedule>()
.HasOne(s => s.DispatchStation)
.WithMany(s => s.RegulatoryScheduleDispatchStations)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
}
The database is created during migration only when I clearly expose the behavior when deleting Restrict
OnDelete (Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict).
Otherwise, it throws an exception:
"Introducing FOREIGN KEY constraint
'FK_RegulatorySchedules_Stations_DispatchStationId' on table
'RegulatorySchedules' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints."
I need the removal of the Station Stations of the table and the table-related properties RegulatorySchedules DispatchStation and DestinationStation exposed to NULL.
But Restrict option there is an exception when you delete a SetNull I can not put.
Tell me how to be?
Described "problem" is not related to Entity Framework - this is restriction of MS SQL Server itself. Table with several FKs may have only one of them with cascade delete.
So, as soon as you need both FKs to have cascade - you should implement such "cleanup" in your code. Set one (or both) FKs to DeleteBehavior.Restrict, and in your controller/service prior to removing Station manually find and delete all related RegulatorySchedule
Dmitry's answer worked perfectly. For any future traveler a working sample of a mapping table down below.
The code is located in the OnModelCreating(ModelBuilder modelBuilder) method in your
DbContext class:
modelBuilder.Entity<AB>()
.HasKey(e => new { e.AId, e.BId});
modelBuilder.Entity<AB>()
.HasOne(e => e.A)
.WithMany(e => e.ABs)
.HasForeignKey(e => e.AId)
.OnDelete(DeleteBehavior.Cascade); // <= This entity has cascading behaviour on deletion
modelBuilder.Entity<AB>()
.HasOne(e => e.B)
.WithMany(e => e.ABs)
.HasForeignKey(e => e.BId)
.OnDelete(DeleteBehavior.Restrict); // <= This entity has restricted behaviour on deletion
I'm trying to create a required:optional relationship between two entities, with the required entity exposing a navigation property to the optional entity, and the optional entity containing the foreign key, used as its primary key. This is what my two entities look like:
class OptionalEntity
{
public string RequiredEntityID { get; set; }
}
class RequiredEntity
{
public string ID { get; set; }
public OptionalEntity Optional { get; set; }
}
And the way I would like to configure them in fluent API is as follows:
// Inside OptionalEntityConfiguration class
public OptionalEntityConfiguration()
{
HasKey(r => r.RequiredEntityID);
}
// Inside RequiredEntityConfiguration class
public RequiredEntityConfiguration()
{
HasKey(r => r.ID);
HasOptional(r => r.Optional)
.WithRequired();
// How can I configure this relationship to use
// the RequiredEntityID property as the foreign key?
HasOptional(r => r.Optional)
.WithRequired(o => o.RequiredEntityID);
// This is invalid because it requires a navigation property, not an ID
HasOptional(r => r.Optional)
.WithRequired()
.HasForeignKey(o => o.RequiredEntityID);
// The HasForeignKey method isn't available here
}
First of all is this possible, and if so what's the correct way to configure this relationship using fluent API?
I think what you are trying to do is use the same key used on the Required Entity on the Optional Entity table, so that they share the same key.
If that is the case, I think you are on the right track. Your entity classes look alright. You can map them like these:
public OptionalEntityConfiguration()
{
HasKey(r => r.RequiredEntityID);
Property(r => r.RequiredEntityID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
public RequiredEntityConfiguration()
{
HasKey(r => r.ID);
HasOptional(r => r.Optional);
}
This implies, however, that there can only be 0 or 1 OptionalEntity for each RequiredEntity.
Try this:
modelBuilder.Entity<RequiredEntity>()
.HasOptional(o => o.Optional)
.WithMany()
.Map(m => m.MapKey("RequiredEntityID"));