Configure a principal key or a set of compatible foreign key properties for this relationship error with EF core - .net-core

I am not sure why I am getting this error:
System.InvalidOperationException: 'The relationship from 'Insured' to 'Quote.Insureds' with foreign key properties {'InsuredID' : string} cannot target the primary key {'InsuredID' : string, 'ProducerID' : string} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.'
I am trying to configure two keys in the Quote model, one for my Insured model and one for my Producer. I attempted to only have one key before and just use foreign keys but that didnt work.
public class Producer
{
[Key]
public string ProducerID { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public class Insured
{
[Key]
public string InsuredID { get; set; }
public string NamedInsured { get; set; }
public string Address1 {get; set;}
public class Quote
{
public string InsuredID { get; set; }
public string ProducerID { get; set; }
[ForeignKey("ProducerID")]
public IList<Producer> Producers { get; set; }
[ForeignKey("InsuredID")]
public IList<Insured> Insureds { get; set; }
In my DB context:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Quote>().HasKey(q => new { q.InsuredID , q.ProducerID});

It is not possible to reference an array with a key
It needs to be an object rather than a collection.
public class Quote
{
public string InsuredID { get; set; }
public string ProducerID { get; set; }
[ForeignKey("ProducerID")]
public Producer Producer { get; set; }
[ForeignKey("InsuredID")]
public Insured Insured { get; set; }
}

Related

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.

ASP.NET Core Conflict with Foreign Key

I have got several models:
Course.cs
public class Course
{
public Guid Id { get; set; }
public ICollection<ApplicationUser> Teacher { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public DateTime CreationDate { get; set; }
public bool IsActive { get; set; }
}
Group.cs
public class Group
{
public Guid Id { get; set; }
public ApplicationUser Mentor { get; set;}
public string DisplayName { get; set; }
public string GroupName { get; set; }
public DateTime StartYear { get; set; }
public string InviteCode { get; set; }
public ICollection<ApplicationUser> Students { get; set; }
public ICollection<Course> Courses { get; set; }
}
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
[Required]
public string Firstname { get; set; }
[Required]
public string Surname { get; set; }
public bool Gender { get; set; }
public DateTime Birthdate { get; set; }
//[Required]
public string InviteCode { get; set; }
public Guid GroupId { get; set; }
[ForeignKey("GroupId")]
public Group CurrentGroup { get; set; }
public ICollection<Group> PastGroups { get; set; }
}
Now when I try to register (using Identity) a user (not even trying to give the user a group) I receive this error:
SqlException: The INSERT statement conflicted with the FOREIGN KEY
constraint "FK_AspNetUsers_Groups_GroupId". The conflict occurred in
database "aspnet-Project_Dojo-3af15f80-8c62-40a6-9850-ee7a296d0726",
table "dbo.Groups", column 'Id'. The statement has been terminated.
In my modelBuilder I have added some logics for the relations between Group, ApplicationUser (Students) and the Foreign Key:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);\\
builder.Entity<ApplicationUser>()
.HasOne(p => p.CurrentGroup)
.WithMany(b => b.Students)
.HasForeignKey(p => p.GroupId);
}
I don't know what this is exactly doing, but I've been browsing some Stackoverflow threads to come to this code (migrations weren't working without it).
I look forward to a solution for my problem. Once again, I'm not doing ANYTHING with the groups yet when registering.
Thanks in advance!
not even trying to give the user a group
Well there's your problem, it's required.
Either provide a group, or make it optional by making the foreign key nullable (Guid? GroupId).
Because it's currently a non-nullable struct, it'll have a default value of all zeroes (Guid.Empty). This FK is not known in your database, resulting in the error you see.

Sqlite.net extentions, Cant get data with GetWithChildren

I'm creating a database with serveral classes as children of other classes. Writing with foreign keys works fine however when i try to get the data back it throws the error:
An unhandled exception of type 'SQLiteNetExtensions.Exceptions.IncorrectRelationshipException' occurred in SQLiteNetExtensions.dll
Additional information: MatchDetail.timeline: At least one entity in a OneToOne relationship must have Foreign Key
This is my creation code:
SQLiteConnection db = new SQLiteConnection(new SQLite.Net.Platform.Win32.SQLitePlatformWin32(), "Matches.db3");
db.CreateTable<ParticipantIdentity>();
db.CreateTable<MatchDetail>();
db.CreateTable<Timeline>();
db.InsertWithChildren(md, true);
var m = db.GetWithChildren<MatchDetail>(matchId, true);
And my class:
[Table("Matches")]
public class MatchDetail
{
[PrimaryKey]
public long matchId { get; set; }
public int mapId { get; set; }
[JsonConverter(typeof(DateTimeConverterFromLong))]
public DateTime MatchCreation { get; set; }
public long matchDuration { get; set; }
public MatchMode matchMode { get; set; }
public MatchType matchType { get; set; }
public string matchVersion { get; set; }
public string platformId { get; set; }
public Queuetype queueType { get; set; }
public Region region { get; set; }
public Season season { get; set; }
[ForeignKey(typeof(Timeline), Name = "TimelineId"), Indexed]
public int timelineId { get; set; }
[OneToOne("TimelineId", CascadeOperations = CascadeOperation.All)]
public Timeline timeline { get; set; }
[ForeignKey(typeof(ParticipantIdentity), Name = "ParticipantId"), Indexed]
public int participantIdentitiesId { get; set; }
[ManyToOne("ParticipantId", CascadeOperations = CascadeOperation.All)]
public List<ParticipantIdentity> participantIdentities { get; set; }
}
The other classes are just an id and some other basic types, I've been trying to work this out but it just doesnt want to work.
Edit:
[ForeignKey(typeof(ParticipantIdentity))]
public int participantIdentitiesId { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<ParticipantIdentity> participantIdentities { get; set; }
With error:
An unhandled exception of type 'SQLiteNetExtensions.Exceptions.IncorrectRelationshipException' occurred in SQLiteNetExtensions.dll
Additional information: MatchDetail.participantIdentities: Unable to find foreign key for OneToMany relationship
You are manually specifying that the foreign key is named 'TimelineId' but in reality the foreign key is named 'timelineId' (note capitalization of the first letter).
Change this:
[ForeignKey(typeof(Timeline), Name = "TimelineId"), Indexed]
public int timelineId { get; set; }
[OneToOne("TimelineId", CascadeOperations = CascadeOperation.All)]
public Timeline timeline { get; set; }
To this:
[ForeignKey(typeof(Timeline)]
public int timelineId { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public Timeline timeline { get; set; }
Explicitly declaring the foreign key name can lead to refactoring issues, so this is the recommended way unless otherwise is strictly required.
The other relationship is declared as a ManyToOne but it's in fact a OneToMany. To make it work you have to change the attribute type and move the foreign key to the other end.
Change the relationship from:
[ForeignKey(typeof(ParticipantIdentity), Name = "ParticipantId"), Indexed]
public int participantIdentitiesId { get; set; }
[ManyToOne("ParticipantId", CascadeOperations = CascadeOperation.All)]
public List<ParticipantIdentity> participantIdentities { get; set; }
With this:
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<ParticipantIdentity> participantIdentities { get; set; }
And add the foreign key to MatchDetail to the ParticipantIdentity class:
[ForeignKey(typeof(MatchDetail)]
public int matchDetailId { get; set; }
Take a look at the SQLite-Net Extensions documentation and sample project for more examples.

Entity Framework Code First Foreign Key Columnname inheritence

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; }
}

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

Resources