Entity Framework Code First Foreign Key Columnname inheritence - ef-code-first

Entity Framework code first (v6) creates a columnname in the database that I don't like. In tablename SharepointMappings it adds columnname: 'SharepointDestination_DestinationId' (foreign key).
It also generates a columnname SharepointDestinationId.
I would like to have 1 column, a foreign key, with the name 'SharepointDestinationId'.
My model looks like this:
public class Destination
{
public int DestinationId { get; set; }
}
public class SharepointDestination : Destination
{
public string UserName { get; set; }
public string Password { get; set; }
public string Domain { get; set; }
public string SiteUrl { get; set; }
public string DocumentLibraryName { get; set; }
public List<SharepointMapping> Mappings { get; set; }
}
public class SharepointMapping
{
public int SharepointMappingId { get; set; }
public string SourceFieldName { get; set; }
public string DestinationFieldName { get; set; }
//[ForeignKey("SharepointDestination")]
public int SharepointDestinationId { get; set; }
//[ForeignKey("SharepointDestinationId")]
public virtual SharepointDestination SharepointDestination { get; set; }
}
//.....
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// To use TPT inheritence
modelBuilder.Entity<SharepointDestination>().ToTable("SharepointDestinations");
//modelBuilder.Entity<SharepointMapping>()
// .HasRequired(m => m.SharepointDestination)
// .WithMany(d => d.Mappings)
// .HasForeignKey(m => m.SharepointDestinationId)
// .WillCascadeOnDelete(true);
}
It doesn't matter if i leave or add the attribute ForeignKey and it also doesn't matter if i make properties virtual or not. Completely deleting both properties on SharepointMapping or giving them a complete other name has no consequences.
I think this has something to do with the inheritence structure. Because it's 'only' a 1-n mapping.
How should I configure EF to have only 1 column with the name 'SharepointDestinationId' which should be a foreign key? (and also have the navigation property and DestinationId property on the SharepointMapping class)

Since the key of SharepointDestination is DestinationId, EF can't automatically figure it out. You could go with the annotation:
[ForeignKey("DestinationId")]
public virtual SharepointDestination SharepointDestination { get; set; }
and remove this:
[ForeignKey("SharepointDestination")]
public int SharepointDestinationId { get; set; }
The fluent should work as well if you comment out the annotation:
modelBuilder.Entity<SharepointMapping>()
.HasRequired(m => m.SharepointDestination)
.WithMany(d => d.Mappings)
.HasForeignKey(m => m.DestinationId)
.WillCascadeOnDelete(true);

The ForeignKey attribute is expecting a property name, not a table column name.
Really, you should be able to do this without any attributes.
The following should work:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
public int ParentId { get; set; }
}

Related

FK for composite key splitted into 2 when table with certain name is added

So I have the following entities defined.
internal class DeliveryArea
{
public string Postcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public ICollection<DeliveryPrice> HasDeliveryPrices { get; set; }
}
internal class DeliveryPrice
{
public uint Id { get; set; }
public DeliveryArea ForDeliveryArea { get; set; }
public string DeliveryAreaPostcode { get; set; }
public string DeliveryAreaState { get; set; }
public string DeliveryAreaCountry { get; set; }
}
and my DbContext is as follow
internal class MyDbContext : DbContext
{
// DbSets.
public DbSet<DeliveryArea> DeliveryAreas { get; set; }
public DbSet<DeliveryPrice> DeliveryPrices { get; set; }
// Overrides.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(#"Data Source=Test.EFCore.db;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region DeliveryArea.
{
var entity = modelBuilder.Entity<DeliveryArea>();
// Setup case-insensitive columns.
entity.Property(i => i.Postcode).HasColumnType("TEXT COLLATE NOCASE");
entity.Property(i => i.State).HasColumnType("TEXT COLLATE NOCASE");
entity.Property(i => i.Country).HasColumnType("TEXT COLLATE NOCASE");
// Setup composite PK.
entity.HasKey(nameof(DeliveryArea.Postcode), nameof(DeliveryArea.State), nameof(DeliveryArea.Country));
}
#endregion
#region DeliveryPrice.
{
var entity = modelBuilder.Entity<DeliveryPrice>();
// DeliveryPrice x DeliveryArea | many-to-one
entity.HasOne(left => left.ForDeliveryArea)
.WithMany(right => right.HasDeliveryPrices)
.HasForeignKey(left => new { left.DeliveryAreaPostcode, left.DeliveryAreaState, left.DeliveryAreaCountry });
}
#endregion
}
}
When the database is generated, EF Core manage to generate appropriate FK that connects both table using the composite key. Everything looks fine and the diagram looks great.
Now, I added the following entity
internal class Currency
{
public uint Id { get; set; }
public ICollection<DeliveryPrice> ForDeliveryPrices { get; set; }
}
and updated DeliveryPrice class as follow
internal class DeliveryPrice
{
public uint Id { get; set; }
// Add the following
public Currency HasCurrency { get; set; }
public uint HasCurrencyId { get; set; }
public DeliveryArea ForDeliveryArea { get; set; }
public string DeliveryAreaPostcode { get; set; }
public string DeliveryAreaState { get; set; }
public string DeliveryAreaCountry { get; set; }
}
My DbContext is updated on top of existing, as follow
internal class MyDbContext : DbContext
{
// DbSets.
public DbSet<Currency> Currencies { get; set; }
// Existing codes remain...
// Overrides.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region Currency.
{
var entity = modelBuilder.Entity<Currency>();
// Currency x DeliveryPrice | one-to-many
entity.HasMany(left => left.ForDeliveryPrices)
.WithOne(right => right.HasCurrency)
.HasForeignKey(right => right.HasCurrencyId);
}
#endregion
// Existing codes remain...
}
}
When the new database is generated, the FK that connects both DeliveryArea and DeliveryPrice table is kinda splitted into 2, as follow
The funny thing is that when the Currencies table is renamed to, say Foo, the FK that connects both DeliveryArea and DeliveryPrice table looks OK.
UPDATE 01:
Normal looking FK
Here's a screenshot of the generated FK that splitted into 2
UPDATE 02:
Upon looking further into the issue, I've found that this is specific to DBeaver only. Viewing the same database file with other database viewer (e.g. DbSchema) does not have the issue.
Any idea what's going on?

How to extend Application User to hold a collection of orders?

I'm trying to extend Application User (using Code-First) to hold a collection of orders, but I'm getting errors.
My Order class is
public class Order
{
public Order()
{
OrderDetails = new HashSet<OrderDetails>();
}
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public string UserId { get; set; }
public bool IsDelivered { get; set; }
public bool IsReturned { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
}
And I'm trying to extend Application user like this
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public string Profession { get; set; }
public string TaxAuthority { get; set; }
public string TaxNumber { get; set; }
public string Address { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
public string MobilePhone { get; set; }
public bool NewsLetterSubscribe { get; set; } = false;
public DateTime TimeStamp { get; set; } = DateTime.Now;
public ICollection<Order> Orders { get; set; }
}
And I'm getting the following errors:
QCMS.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
QCMS.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
Can you please help me to solve this problem?
UPDATE:
I'm using two db contexts. The one provided for Individual User Account (when the project is first created) and a second one named "qvModel" that is for all other database classes of my project.
public partial class qvModel : DbContext
{
public qvModel()
: base("name=qvModel")
{
}
//APPSETTINGS
public virtual DbSet<AdminLog> AdminLog { get; set; }
public virtual DbSet<WebLog> WebLog { get; set; }
//LANGUAGES
public virtual DbSet<Language> Languages { get; set; }
.
.
.
public virtual DbSet<Order> Orders { get; set; }
public virtual DbSet<OrderDetails> OrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Precision attribute for decimals
Precision.ConfigureModelBuilder(modelBuilder);
modelBuilder.Entity<Language>()
.HasMany(e => e.Brochures)
.WithRequired(e => e.Language)
.WillCascadeOnDelete(true);
.
.
.
modelBuilder.Entity<Order>()
.HasMany(c => c.OrderDetails)
.WithRequired(c => c.Order)
.WillCascadeOnDelete(true);
modelBuilder.Entity<ApplicationUser>()
.HasMany(c => c.Orders)
.WithRequired(c => c.User)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
}
I found a solution that is very simple.
The solution is to inherit from IdentityDbContext like this
public class qvModel : IdentityDbContext<ApplicationUser>
{
public qvModel()
: base("name=qvModel")
{
}
I was also missing the following line from OnModelCreating
base.OnModelCreating(modelBuilder);
After these changes, my migration is working and I stopped getting the errors I mentioned.

Many to many relation between Identity and custom table. EF7 - Code first

How can I make many to many relation between AspNetRoles from Identity 3.0 and my custom table? I want simple 3 table, with both PermissionId and RoleId, something like AspNetUsersRole. I have something like this:
public class Permission
{
public int PermissionId { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicationRole> Roles { get; set; }
}
public class ApplicationRole : IdentityRole
{
public virtual ICollection<Permission> Permissions { get; set; }
}
But when I want to add migration, I got error:
Unable to determine the relationship represented by navigation property 'ApplicationRole.Permissions' of type 'ICollection<Permission>'. Either manually configure the relationship, or ignore this property from the model.
EF Core (EF7) does not currently support many to many relationship without a join entity. (Reference)
So, what you should do is to create an entity class for the join table and mapping two separate one-to-many relationships. Like;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Regarding to this question answer, it can be done more easily like this-
class Photo
{
public int Id { get; set; }
public ICollection<PersonPhoto> PersonPhotos{ get; set; }
}
class PersonPhoto
{
public int PhotoId { get; set; }
public Photo Photo { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
}
class Person
{
public int Id { get; set; }
public ICollection<PersonPhoto> PersonPhotos{ get; set; }
}
Be sure to configure PersonPhoto with a composite key:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonPhoto>().HasKey(x => new { x.PhotoId, x.PersonId });
}
To navigate, use a Select:
// person.Photos
var photos = person.PersonPhotos.Select(c => c.Photo);
Add This namespace-
using Microsoft.AspNetCore.Identity;
public class Permission
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PermissionId { get; set; }
public string Name { get; set; }
public string UserIdFK { get; set; } //Foreign Key of Identity tbl
[ForeignKey("UserIdFK")]
public IdentityUser UserDetail { get; set; }
}
That's it, Happy coding :)

Foreign key relationship

I'm trying to setup a foreign key using the following two classes.
I want to use pAcqType as an enum and store the names of the types in another table. How should I setup my classes to do this?
public class Property
{
[Key]
public int pID { get; set; }
public string pAddress { get; set; }
public string pCounty { get; set; }
public string pCity { get; set; }
public string pState { get; set; }
public string pzip { get; set; }
public virtual PropertyAcquisitionType pAcqType { get; set; } <-- foreign key
}
public class PropertyAcquisitionType
{
[Key]
public int patID { get; set; }
public string patName { get; set; }
}
UPDATE
Dan got me thinking. And I tried the following and it seems to have worked out.
It setup the foreign key on the table like I wanted. And it didn't even ask for an inverse on the other table.
public int? pAcqType { get; set; }
[ForeignKey("pAcqType")]
public PropertyAcquisitionType patID { get; set; }
Is the foreign key required (NOT NULL in the database)?
public int pAcqTypeId { get; set; }
[ForeignKey("pAcqTypeId")]
public virtual PropertyAcquisitionType pAcqType { get; set; }
Otherwise,
public int? pAcqTypeId { get; set; }
[ForeignKey("pAcqTypeId")]
public virtual PropertyAcquisitionType pAcqType { get; set; }
Then in your other class, add an inverse relationship:
public class PropertyAcquisitionType
{
[Key]
public int patID { get; set; }
public string patName { get; set; }
[InverseProperty("pAcqType")]
public virtual ICollection<Property> pOfThisType { get; set; }
}
Here is one way you could define the relationship using the fluent API (without attributes in the entity classes). Note with this method, you should not need to add a properties property on the PropertyAcquisitionType entity to satisfy the inverse side of the relationship, because the .WithMany() tells EF what it needs to know:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Property>()
.HasKey(x => x.pID)
.HasRequired(x => x.pAcqType) // or HasOptional if using int?
.WithMany() // or WithMany(x => x.pOfThisType) if you want to add / keep the inverse property
.HasForeignKey(x => x.pAcqTypeId)
;
}
}

How to configure multiple Foreignkey referancing to same table from main table using EF Code First Fluient API

I have to design a web application using existing database of desktop application. As per existing database i have below class
public class Company
{
#region Primitive Properties
public virtual int CompanyID { get; set; }
public virtual string CompanyName { get; set; }
public virtual bool IsCustomer { get; set; }
public virtual string CustomerCode { get; set; }
public virtual bool IsPotentialCustomer { get; set; }
public virtual bool IsSupplier { get; set; }
public virtual string SupplierCode { get; set; }
public virtual bool IsPotentialSupplier { get; set; }
public CompanyCategoryCodes CustomerCategoryCode { get; set; }
public CompanyCategoryCodes SupplierCategoryCode { get; set; }
public CountryCode CountryCode { get; set; }
}
public class CompanyCategoryCodes
{
public virtual int CategoryID { get; set; }
public virtual string CategoryCodes { get; set; }
public virtual bool PotentialCustomer { get; set; }
public virtual bool PotentialSupplier { get; set; }
public virtual System.DateTime LastModifiedDate { get; set; }
public virtual bool Manufacturer { get; set; }
}
public class CountryCode
{
public virtual int CountryCodeID { get; set; }
public virtual string Code { get; set; }
public virtual string Description { get; set; }
public virtual bool DefaultCode { get; set; }
public virtual bool EECVATApplies { get; set; }
public virtual System.DateTime LastModifiedDate { get; set; }
public virtual bool FixedAddressFormat { get; set; }
}
EF Code first default framework is creating Foreignkey with name "CustomerCategoryCode_CategoryID" , "SupplierCategoryCode_CategoryID", "CountryCode_CountryCodeID". I want this Foreignkey name to be consistance with my old database tables e.g. "CustomerCategoryCodeID", "SupplierCategoryCodeID", "CountryCodeID". How can i do it using EF Code First Fluient API. I try to do it using Fluient API Mapping but i got error for "SupplierCategoryCode_CategoryCodeID" as "CustomerCategoryCode_CategoryID" is also locating to same table "CompanyCategoryCode". Also if is there any option available using Data Annotation then also let me know how to achieve this.
You must manually remap each navigation property to define its key. Something like:
modelBuilder.Entity<Company>()
.HasRequired(c => c.CountryCode)
.WithMany()
.HasForeignKey("CountryCodeID");
modelBuilder.Entity<Company>()
.HasMany(c => c.CustomerCategoryCode)
.WithOptional()
.HasForeignKey("CustomerCategoryCodeID")
.WillCascadeOnDelete(false);
modelBuilder.Entity<Company>()
.HasMany(c => c.SupplierCategoryCode)
.WithOptional()
.HasForeignKey("SupplierCategoryCodeID")
.WillCascadeOnDelete(false);
It is not possible with data annotations unless you define navigation property and foreign key property in each dependent entity like:
public class Company
{
...
[ForeignKey("CountryCode")]
public virtual int CountryCodeID { get; set; }
public CountryCode CountryCode { get; set; }
}
Inside your context class, you will need to override OnModelCreating and map the keys, it should look something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasRequired(c => c.CustomerCategoryCode)
.WithMany()
.Map(mc => mc.MapKey("CustomerCategoryCodeID"));
}

Resources