EF 6 Inserting entity with provided ID still generates ID - c#

I have an entity with ID field
public class AgentOrder : BaseEntity
{
public int Id { get; set; }
}
public class AgentOrderMap : EntityTypeConfiguration<AgentOrder>
{
public AgentOrderMap()
{
ToTable("AgentOrder");
HasKey(m => m.Id);
Property(m => m.Status);
}
}
I create a new entity and define Id property as well. But when I save changes the new entity is created not with Id I've defined but with automatically generated one.
Is it normal behavior of key property?

Since you are using Fluent Configurations, you just need to turn off the default identity insert configuration:
So this:
public AgentOrderMap()
{
ToTable("AgentOrder");
HasKey(m => m.Id);
Property(m => m.Status);
}
Becomes:
public AgentOrderMap()
{
ToTable("AgentOrder");
HasKey(m => m.Id);
Property(m => m.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(m => m.Status);
}
Note that your database needs to support this functionality and have identity inserts turned off as well.

By convention, the id property uses Identity, so, it cannot be manually inserted. Configure it like this:
Property(l => l.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

Related

.NET Core 6 migration error: Cannot use table 'AspNetRoleClaims' for entity type 'IdentityRoleClaim<string>'

After migrating a .NET Core 3.1 project to .NET Core 6, I receive the following error whenever my DbContext class is called:
InvalidOperationException: Cannot use table 'AspNetRoleClaims' for entity type 'IdentityRoleClaim' since it is being used for entity type 'AspNetRoleClaims' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'IdentityRoleClaim' on the primary key properties and pointing to the primary key on another entity type mapped to 'AspNetRoleClaims'.
After much searching, I have been unable to find a solution to this issue. The code I used in 3.1 worked fine, so I can only assume something has changed between then and now. Here is the code for my db context:
public partial class MyProjectContext : IdentityDbContext<IdentityUser>
{
public MyProjectContext()
{
}
public MyProjectContext(DbContextOptions<MyProjectContext> options)
: base(options)
{
}
public virtual DbSet<AspNetRoleClaims> AspNetRoleClaims { get; set; }
public virtual DbSet<AspNetRoles> AspNetRoles { get; set; }
public virtual DbSet<AspNetUserClaims> AspNetUserClaims { get; set; }
public virtual DbSet<AspNetUserLogins> AspNetUserLogins { get; set; }
public virtual DbSet<AspNetUserRoles> AspNetUserRoles { get; set; }
public virtual DbSet<AspNetUserTokens> AspNetUserTokens { get; set; }
public virtual DbSet<AspNetUsers> AspNetUsers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AspNetRoleClaims>(entity =>
{
entity.HasIndex(e => e.RoleId);
entity.Property(e => e.RoleId).IsRequired();
entity.HasOne(d => d.Role)
.WithMany(p => p.AspNetRoleClaims)
.HasForeignKey(d => d.RoleId);
});
modelBuilder.Entity<AspNetRoles>(entity =>
{
entity.HasIndex(e => e.NormalizedName, "RoleNameIndex")
.IsUnique()
.HasFilter("([NormalizedName] IS NOT NULL)");
});
modelBuilder.Entity<AspNetUserLogins>(entity =>
{
entity.HasKey(e => new { e.LoginProvider, e.ProviderKey });
});
modelBuilder.Entity<AspNetUserRoles>(entity =>
{
entity.HasKey(e => new { e.UserId, e.RoleId });
});
modelBuilder.Entity<AspNetUserTokens>(entity =>
{
entity.HasKey(e => new { e.UserId, e.LoginProvider, e.Name });
});
modelBuilder.Entity<AspNetUsers>(entity =>
{
entity.HasIndex(e => e.NormalizedUserName, "UserNameIndex")
.IsUnique()
.HasFilter("([NormalizedUserName] IS NOT NULL)");
});
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
optionsBuilder.UseSqlServer(configuration.GetConnectionString("MyConnectionString"));
}
}
I have following from the Microsoft documentation here
I have tried changing the class declaration to
public partial class IdentityDbContext : IdentityDbContext<IdentityUser, IdentityRole, string>
and received the same error.
I have also tried the following to address the foreign key issue
public partial class IdentityDbContext<TUser>
: IdentityDbContext<TUser, IdentityRole, string>
where TUser : IdentityUser
However, it results in a compiler error.
I have even tried stating the foreign key explicitly in the model declaration:
modelBuilder.Entity<AspNetRoleClaims>(entity =>
{
entity.HasIndex(e => e.RoleId);
entity.Property(e => e.RoleId).IsRequired();
entity.HasOne(d => d.Role)
.WithMany(p => p.AspNetRoleClaims)
.HasForeignKey(d => d.RoleId);
//Tried to declare the relationship here
entity.HasOne(d => d.RoleId)
.WithOne(p => p.*) //compiler error regardless of entry
.HasForeignKey(d => d.*) //compiler error regardless of entry
});
Unsurprisingly, this didn't work either; I do not have any IdentityUser in my actual database.
So what am I missing here? I cannot find an easy way to troubleshoot this. It seems to trip the error the moment the Context class has finished processing, so that is where I suspect the problem may lie.
Any thoughts on how to fix this?
Edit: Here is the IdentityUser class. It is the default included with the framework.
namespace Microsoft.AspNetCore.Identity
{
//
// Summary:
// The default implementation of Microsoft.AspNetCore.Identity.IdentityUser`1 which
// uses a string as a primary key.
public class IdentityUser : IdentityUser<string>
{
//
// Summary:
// Initializes a new instance of Microsoft.AspNetCore.Identity.IdentityUser.
//
// Remarks:
// The Id property is initialized to form a new GUID string value.
public IdentityUser()
{
Id = Guid.NewGuid().ToString();
SecurityStamp = Guid.NewGuid().ToString();
}
//
// Summary:
// Initializes a new instance of Microsoft.AspNetCore.Identity.IdentityUser.
//
// Parameters:
// userName:
// The user name.
//
// Remarks:
// The Id property is initialized to form a new GUID string value.
public IdentityUser(string userName)
: this()
{
UserName = userName;
}
}
}
The dotnet EF scaffold command doesn't map many to many tables in EF Core 6, see issue https://github.com/ErikEJ/EFCorePowerTools/issues/1191.
Use EFCorePowerTools instead and enable Many to Many on the Advanced options dialog resolves this.
Using .NET Core 6.0 and EF Core 6.0 (Adding Identity to existing database)
After looking through countless forums, tutorials, discussion pages and documentation, here's the YouTube video that finally helped me resolve this issue:
https://www.youtube.com/watch?v=5X9ogJVWAjE&ab_channel=DataVids
Essentially, you should have a DbContext file that was provided from Identity (we'll call Id_DbContext) that inherits from IdentityDbContext. This will be used to build the identity tables in the database. Your existing DbContext file (we'll call Existing_DbContext) will inherit from Id_DbContext and will have everything in there from your existing database.
/////////Identity Context File Class Header////////////////
public class Id_DbContext : IdentityDbContext
/////////Existing Context File Class Header////////////////
public partial class Existing_DbContext : Id_DbContext
Hope this helps and saves you some hours of searching for a solution. .NET Core 6.0 and EF Core 6.0 are relatively new so there's not a lot of online support available just yet.
Good luck!

How to make an OwnsOne property in EF Core 3.0 required when mapping to SQL Server columns?

I have a main entity Profile that has a property Name that is a value object. The Name object has two properties First and Last. When I use the Fluent API to map the Name objects properties to columns within the Profile table I specify that they are required. When I create the migration it says nullable is true. I assume it has to do with the fact that in EF Core 3.0 owned entities are now optional but how do I tell EF that they are actually required?
public class Profile
{
public Name Name { get; private set; }
...
}
public class Name
{
public string First { get; }
public string Last { get; }
...
}
public override void Configure(EntityTypeBuilder<Profile> builder)
{
base.Configure(builder);
builder.OwnsOne(
navigationExpression: p => p.Name,
buildAction: n =>
{
n.Property(n => n.First)
.HasColumnName("NameFirst")
.HasMaxLength(25)
.IsRequired();
n.Property(n => n.Last)
.HasColumnName("NameLast")
.HasMaxLength(25)
.IsRequired();
});
}
Any help you can provide would be great.
EF Core 5
In addition to set .IsRequired() on the required properties within the ValueObject,
you need to configure the navigation as required after x.OwnsOne(...):
builder.OwnsOne(o => o.Address, a =>
{
a.WithOwner();
a.Property(p => p.Street)
.IsRequired();
a.Property(p => p.ZipCode)
.IsRequired();
a.Property(p => p.City)
.IsRequired();
}).Navigation(p => p.Address).IsRequired();
=============^========================================^
Issue: https://github.com/dotnet/efcore/issues/12100
Credits to: #AndriySvyryd
I reached out to the EF Core team and currently the only way to do this would be to manually change the migration that is created to set nullable = false. It has been flagged as a feature request so let's hope one day they get it fixed!

Problem with master/detail tables and Entity Framework

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.

EntityFramework foreign key as primary key with fluent API

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"));

Use enum as FK in EF6

We have a enum Supplier
But now we need to also have some Domain data on that relation
So in 99.9% in the domain code we doe operations on the enum like product.Supplier == Suppliers.FedEx
But now we also have added product.SupplierInfo.CanAdjustPickupTime where SupplierInfo is a Entity and not just a simple enum type.
I have tried these configs
Property(p => p.Supplier)
.IsRequired()
.HasColumnName("SupplierId");
HasRequired(p => p.SupplierInfo)
.WithMany()
.HasForeignKey(p => p.Supplier); //I have also tried casting to int doing .HasForeignKey(p => (int)p.Supplier)
This will fail with
The ResultType of the specified expression is not compatible with the
required type. The expression ResultType is
'MyApp.Model.Suppliers' but the required type is
'Edm.Int32'. Parameter name: keyValues[0]
Also tried
Property(l => l.Supplier)
.IsRequired()
.HasColumnName("SupplierId");
HasRequired(p => p.SupplierInfo)
.WithMany()
.Map(m => m.MapKey("SupplierId"));
This will offcourse give the good old
One or more validation errors were detected during model generation:
SupplierId: Name: Each property name in a type must be unique.
Property name 'SupplierId' is already defined.
I could offcourse define SupplierId as a Property use that with HasForeignKey But then I need to change to .SuppliedId == (int)Suppliers.FedEx etc. Not really a solution.
I could also add a property enum that uses the SupplierId property as backing field, but this will not work with Expressions since it needs to use real mapped DB properties
Any ideas?
I have classes:
public class Agreement
{
public int Id { get; set; }
public AgreementStateTypeEnum AgreementStateId { get; set; }
}
public class AgreementState
{
public int Id { get; set; }
public string Title { get; set; }
}
context:
public class AgreementContext :DbContext
{
public AgreementContext() : base("SqlConnection") { }
public DbSet<Agreement> Agreements { get; set; }
}
In method OnModelCreating I wrote nothing.
My enum:
public enum AgreementStateTypeEnum : int
{
InReviewing = 1,
Confirmed = 2,
Rejected = 3
}
In database: in table Agreements I have foreign key AgreementStateId - it is link to table AgreementStates.
Everything is working. For example:
var temp = context.Agreements.First(x => x.AgreementStateId == AgreementStateTypeEnum.Confirmed);
I use enum how foreign key.
Finally I found the problem. (I'm using EF6, NET 4.5)
So, if you create a type Enum in your code, you couldn't create a relationship with other property virtual.
//This is wrong, when do you create a foreignkey using a type enum
//Do You should remove that's code on in your class Map.
HasRequired(p => p.SupplierInfo)
.WithMany()
.HasForeignKey(p => p.Supplier); //I have also tried casting to int doing
.HasForeignKey(p => (int)p.Supplier)
If did you created a type enum it means that you don't need for a table return data throught for a join in EF.
So, the correct code it is:
public class MyClass{
public enum myEnumType {
FedEx,
Olther
}
public int id {get;set;}
public myEnumType Supplier {get;set;}
}
//My class Map (using Fluent...)
public class MyClassMap {
HasKey(t => t.Id);
Property(t => t.Id).HasColumnName("Id");
//The type [supplier] should be [int] in database.
Property(t => t.Supplier).HasColumnName("supplier");
//That's all, you don't need write relationship, int this case
//Because, when the data returns, the EF will to do the conversion for you.
}
I hope that's useful
The best way I have found to deal with this scenario is to map Supplier as a regular domain object and create a separate class of known supplier IDs.
public class KnownSupplierIds
{
public const int FedEx = 1;
public const int UPS = 2;
// etc.
}
if (product.Supplier.SupplierId == KnownSupplierIds.Fedex) { ... };
When your code needs to check the supplier, it can compare the IDs; when you need additional info from the domain model you just load the Supplier. The reason I prefer using a class of constants instead of an enum is that the pattern works for string comparisons also and there's no need to cast.

Categories

Resources