I am using EF6. I have two models PowerLine and PowerSource. PowerSource has a foreign key column that points to PowerLine. The problem is that the PowerLine object is populating with List of PowerSource but the SourcePowerLine navigation property of PowerSource is always NULL. My models are as follows :
public class PowerLine
{
[Key]
public int ID { get; set; }
public virtual ICollection<PowerSource> PowerSources { get; set; }
}
public class PowerSource
{
[Key]
public int ID { get; set; }
public int SourcePowerLineID {get;set;}
[ForeignKey("SourcePowerLineID")]
public virtual PowerLine SourcePowerLine { get; set; }
}
I tried Column(Order) attribute as well to set the correct order also I tried to use Fluent API like as follows :
modelBuilder.Entity<PowerSource>()
.HasRequired(c => c.SourcePowerLine )
.WithMany(b => b.PowerSources)
.HasForeignKey(c => c.SourcePowerLineID);
Your [ForeingKey()] attribute is not really pointing to anywhere, try:
[ForeignKey("PowerLine")]
public int SourcePowerLineID { get; set; }
Related
This are my entities:
[Table("AktAdr")]
public class ActivityAddress
{
[Column("AktID")]
public int ActivityId { get; set; }
[Column("AktAdr")]
public string AddressId { get; set; }
[Column("AktCID")]
public int LoginUserId { get; set; }
public LoginUser LoginUser { get; set; }
[Column("AdrGrp")]
public int AdrGrpId { get; set; }
}
[Table("CContact")]
public class LoginUser
{
[Column("ID")]
public int? Id { get; set; }
[Column("txt1")]
public string PersonelNumber { get; set; }
}
and this is the definition of the relationship:
modelBuilder.Entity<ActivityAddress>()
.HasOne(e => e.LoginUser)
.WithMany()
.HasForeignKey(e => e.LoginUserId)
.HasPrincipalKey(e => e.Id)
.IsRequired(false);
Now, when I execute
var act = ctx.ActivityAddresses
.Include(x => x.LoginUser)
.FirstOrDefault(x => x.ActivityId == 549841);
I get the exception
Microsoft.Data.SqlClient.SqlException: Invalid column name 'LoginUserId1'
I wonder why the LoginUserId1 column is in the query. Isn't it defined anywhere?
Can someone help me what I'm doing wrong or where does EF Core get this column from?
you will have to fix LoginUser. It needs a key, and key can't be nullable, since LoginUserId is not nullable, and EF creates shadow LoginUserId1 property as a key. Make Id not nullable or LoginUserId nullable. And add ActivityAddresses as navigation property too.
[Table("CContact")]
public class LoginUser
{
[Column("ID")]
[Key]
public int Id { get; set; }
[Column("txt1")]
public string PersonelNumber { get; set; }
public virtual ICollection<ActivityAddress> ActivityAddresses {get; set;}
}
and you will have to fix the fluent APIs to make Id as a key and include new navigation property
I have cross relations with the same table, in 3rd object.
When I try to insert new object, got an error :
Multiplicity constraint violated. The role
'OrgOwners_Organisation_Target' of the relationship
'GBankDataSource.OrgOwners_Organisation' has multiplicity 1 or 0..1.
I've tried to annotate [ForeignKey("...")] in any of the classes, but nothing happend. EF allways choose one field (OrgRefID in this sample) and use it or both relations, while OrgID are not used.
public class OrganisationInfo
{
[Key]
public int OrgID { get; set; }
...
public virtual List<OrgOwners> OrgOwners { get; set; } // object that throws error
}
public class OrgOwners
{
[Key]
public int OrgOwnerID { get; set; }
public int OrgID { get; set; } //Suppose to be a ForeignKey for (OrganisationInfo OrgOwners List)
public int? OrgRefID { get; set; }
...
[ForeignKey("OrgRefID")]
public virtual OrganisationInfo Organisation { get; set; } //(Suppose to use OrgRefID as ForeignKey)
}
When I add a record to OrgOwners without Organisation ( Organisation =null) - it is OK. But when I do
var first = new OrganisationInfo(); //First organisation DB.OrganisationInfoes.Add(first);
var nextOrg = new OrganisationInfo(); //second organisation
first.OrgOwners = new list();
var Owner = new OrgOwners(); Owner.Organsiation = nextOrg;
first.OrgOwners.Add(Owner); // Add Owner with the second organisation to the First one.
I got an error.
Multiplicity constraint violated.
OrgOwner.Organisation - is NOT the same OrganisationInfo as in root of OrgOwners list. It must be different OrganisationInfo items, related to OrgRefID ForeignKey.
It's because EF by default automatically "pairs" the navigation properties where possible to form a relationship. In your case, it pairs OrganizationInfo.OrgOwners collection navigation property with OrgOwners.Organization reference navigation property, hence takes and uses the associated with it OrgRefID FK.
One way to resolve the issue is to add a second reference navigation property to OrgOwners and associate it with the OrgID property via ForeignKey attribute and OrganizationInfo.OrgOwners collection navigation property via InverseProperty attribute:
public int OrgID { get; set; } //Suppose to be a ForeignKey for (OrganisationInfo OrgOwners List)
[ForeignKey("OrgID")]
[InverseProperty("OrgOwners")]
public virtual OrganisationInfo OwnerOrganization { get; set; }
To do that without changing the entity model, you should configure the relationship via fluent API:
modelBuilder.Entity<OrganisationInfo>()
.HasMany(e => e.OrgOwners)
.WithRequired() // no inverse navigation property
.HasForeignKey(e => e.OrgID); // <--
Full worked example:
public class OrganisationInfo
{
[Key]
public int OrgID { get; set; }
public virtual List<OrgOwners> OrgOwners { get; set; }
}
public class OrgOwners
{
[Key]
public int OrgOwnerID { get; set; }
public int OrgID { get; set; }
public int? OrgRefID { get; set; }
[ForeignKey("OrgRefID")]
public virtual OrganisationInfo Organisation { get; set; }
}
modelBuilder.Entity<OrganisationInfo>()
.HasMany(e => e.OrgOwners)
.WithRequired()
.HasForeignKey(e => e.OrgID);
I have a UserProfile class
[Key]
public int UserProfileId { get; set; }
public string AppUserId { get; set; }
...code removed for brevity
[Required]
public NotificationMethod NotificationMethod { get; set; }
public List<PrivateMessage> PrivateMessages { get; set; }
public List<Machine> OwnedMachines { get; set; }
public bool IsProfileComplete { get; set; }
public byte[] Avatar { get; set; }
public string AvatarUrl { get; set; }
public string GetFullName()
{
return $"{FirstName} {LastName}";
}
}
I also have a PrivateMessage class
public class PrivateMessage
{
[Key]
public int Id { get; set; }
public int MessageToUserId { get; set; }
public int MessageFromUserId { get; set; }
public DateTime DateSent { get; set; }
public string Message { get; set; }
}
I set up a simple test to pull the user profile out with various includes. The PrivateMessages always errors. Here is a sample method that errors.
public static UserProfile GetUserProfileIncluding(string appUserId)
{
using (RestorationContext)
{
//RestorationContext.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
return RestorationContext.MemberProfiles.Where(m => m.AppUserId == appUserId)
.Include(m=> m.PrivateMessages)
.FirstOrDefault();
}
}
The error noted is
InnerException {"Invalid column name 'UserProfile_UserProfileId'.\r\nInvalid column name 'UserProfile_UserProfileId'."} System.Exception {System.Data.SqlClient.SqlException}
Which I don't understand, neither table has a column "UserProfile_UserProfileId"
If I use the property OwnedMachines instead of PrivateMessages, it works perfectly fine (well not really, its only pulling in 4 records when there are 6 that match but I can figure that out later).
public static UserProfile GetUserProfileIncluding(string appUserId)
{
using (RestorationContext)
{
return RestorationContext.MemberProfiles.Where(m => m.AppUserId == appUserId)
.Include(m=> m.OwnedMachines)
.FirstOrDefault();
}
}
And you can see below, Machine is set up exactly like PrivateMessage, albeit it has two UserProfiles instead of one
public class Machine
{
[Key]
public int MachineId { get; set; }
public int OwnerProfileId { get; set; }
public int SerialNumber { get; set; }
public string YearofManufacture { get; set; }
public string ModelName { get; set; }
public Manufacturer Manufacturer { get; set; }
public DateTime DateAcquired { get; set; }
}
I've spent far to much time on this now. Does it have something to do with the fact that I have two UserProfile Id int properties in PrivateMessage? (MessageToUserId & MessageFromUserId). I originally had these set as foreign keys with a UserProfile property in there as well like this
[ForeignKey("MessageToProfile")]
public int MessageToUserId { get; set; }
public UserProfile MessageToProfile { get; set; }
[ForeignKey("MessageFromProfile")]
public int MessageFromUserId { get; set; }
public UserProfile MessageFromProfile { get; set; }
But I removed them thinking they may have been the source of the error, but apparently not.
UPDATE:
After a bunch more trial and error, it is apparent that the current method will always err as the method is looking for a navigable property which doesn't exist. Since I have the two int properties in PrivateMessage, when trying to include those in the UserProfile object, I will need to filter then by MessageToUserId and then include them. Not sure how to filter and include.
Using this method should work;
public static UserProfile GetProfileForLoggedInUser(string appUserId)
{
using (RestorationContext)
{
RestorationContext.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
var profile= RestorationContext.MemberProfiles.Include(m => m.OwnedMachines)
.FirstOrDefault(m => m.AppUserId == appUserId);
var pms = RestorationContext.PrivateMessages.Where(m => m.MessageToUserId == profile.UserProfileId).ToList();
if (profile != null) profile.PrivateMessages = pms;
return profile;
}
}
But it gives the same invalid column error UserProfile_UserProfileID.
Here is the TSql
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[MessageToUserId] AS [MessageToUserId],
[Extent1].[MessageFromUserId] AS [MessageFromUserId],
[Extent1].[DateSent] AS [DateSent],
[Extent1].[Message] AS [Message],
[Extent1].[UserProfile_UserProfileId] AS [UserProfile_UserProfileId]
FROM [RestorationContext].[PrivateMessages] AS [Extent1]
WHERE [Extent1].[MessageToUserId] = #p__linq__0
Since this is just querying the PrivateMessage table WHY is it looking for that UserProfileId, it has nothing to do with this table. Here are the table properties from SSMS
Where is that UserProfileID crap coming from?
Your Machine inclusion works because the Machine class has only one foreign key of UserProfile.
You have 2 foreign keys to the same table in PrivateMessage class, naturally, you would need 2 ICollection navigation properties in your UserProfile class. EntityFramework didn't know which foreign key to use in your PrivateMessage class for loading your ICollection<PrivateMessage> property in your UserProfile class.
public ICollection<PrivateMessage> FromPrivateMessages { get; set; }
public ICollection<PrivateMessage> ToPrivateMessages { get; set; }
In your context class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PrivateMessage>()
.HasRequired(m => m.MessageFromProfile)
.WithMany(t => t.FromPrivateMessages)
.HasForeignKey(m => m.MessageFromUserId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<PrivateMessage>()
.HasRequired(m => m.MessageToProfile)
.WithMany(t => t.ToPrivateMessages)
.HasForeignKey(m => m.MessageToUserId)
.WillCascadeOnDelete(false);
}
UPDATE
EF uses convention over configuration, and by having navigation properties of UserProfile in your PrivateMessage class will imply a relationship and EF will try to find a foreign key in the form of <Navigation Property Name>_<Primary Key Name of Navigation Property type>, which gives you UserProfile_UserProfileId.
You should be wondering why UserProfile_UserProfileId instead of UserProfile_MessageToUserId or UserProfile_MessageFromUserId at this point. That's because of your foreign key attribute, telling EF to use the UserProfileId property in your UserProfile class.
What you can do now is, remove the foreign key attributes like this
public int MessageToUserId { get; set; }
public UserProfile MessageToProfile { get; set; }
public int MessageFromUserId { get; set; }
public UserProfile MessageFromProfile {get; set; }
and add another ICollection and do the modelBuilder configuration like how I stated before the update.
I have the following design.
As we can see from this image, an Episode will have multiple EpisodePatients, and each of these EpisodePatients, will point to one Episode.
Here are my two models corresponding to the tables.
public class EpisodeModel
{
public int ID { get; set; }
public virtual EpisodePatientModel EpisodePatient { get; set; }
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public virtual EpisodeModel Episode { get; set; }
}
How do I setup the One to Many relationship between EpisodeModel and EpisodePatientModel?
Since EpisodeModel does not contain a foreign key to EpisodePatient, I cannot do the following.
modelBuilder.Entity<EpisodeModel>().HasRequired(r => r.EpisodePatient).WithMany().HasForeignKey() //No foreign key
I have tried this.
modelBuilder.Entity<EpisodeModel>().HasRequired(r => r.EpisodePatient);
But with this approach, the EpisodeModel is not Lazy loaded when loading all EpisodePatientModels form the DB
First of all your model does not reflect what you say. If there is one-to-many relationship between EpisodeModel and EpisodePatientModel you must have collection of EpisodePatientModel. And you are missing foreign key property at EpisodePatientModel:
public class EpisodeModel
{
public int ID { get; set; }
public virtual ICollection<EpisodePatientModel> EpisodePatients { get; set; } // Must be collection
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public int EpisodeID { get; set; } // Foreign key to Episode
public virtual EpisodeModel Episode { get; set; }
}
Then after your models are correct, mappings with Fluent API is easy to understand, map them as you say: one EpisodeModel can have many EpisodePatientModel:
modelBuilder.Entity<EpisodeModel>()
.HasMany(r => r.EpisodePatients)
.WithRequired(m => m.Episode)
.HasForeignKey(m => m.EpisodeID);
Or you can map reverse of this. Adding one of these two is enough:
modelBuilder.Entity<EpisodePatientModel>()
.HasRequired(r => r.Episode)
.WithMany(m => m.Episode)
.HasForeignKey(m => m.EpisodeID);
I understood that you need to have a foreign key in the EpisodePatient table that refers to the Episode table Id, so I think you can solve it like this:
public class EpisodeModel
{
public int ID { get; set; }
public virtual IEnumerable<EpisodePatientModel> EpisodePatients { get; set; }
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public int EpisodeID { get; set; }
public virtual EpisodeModel Episode { get; set; }
}
And then, your configuration should go like this:
modelBuilder.Entity<EpisodePatientModel>().HasRequired(r => r.Episode).WithMany(e => e.EpisodePatients).HasForeignKey(r => r.EpisodeID);
I'm fairly new to Entity Framework and feel more in control using the Code-First pattern rather than DB-First.
I was wondering what is more preferred when it comes to programmatically setting up ForeignKey relations between the entities.
Is it better to declare a FK_ property in the class which relates to the another class or is it better to declare an IEnumerable<> property in the class that gets related to?
public class IRelateToAnotherClass
{
...
public int FK_IGetRelatedToByAnotherClass_ID { get; set; }
}
or
public class IGetRelatedToByAnotherClass
{
...
public IEnumerable<IRelateToAnotherClass> RelatedTo { get; set; }
}
It all depends on what type of relationships you want between your entities (one-to-one, one-to-many, many-to-many); but, yes, you should declare foreign key properties. Check out this site for some examples.
Here's a one-to-many for your two classes:
public class IRelateToAnotherClass
{
public int Id { get; set; } // primary key
public virtual ICollection<IGetRelatedToByAnotherClass> IGetRelatedToByAnotherClasses { get; set; }
}
public class IGetRelatedToByAnotherClass
{
public int Id { get; set; } // primary key
public int IRelateToAnotherClassId { get; set; } // foreign key
public virtual IRelateToAnotherClass IRelateToAnotherClass { get; set; }
}
and with some Fluent API mapping:
modelBuilder.Entity<IGetRelatedToByAnotherClass>.HasRequired<IRelateToAnotherClass>(p => p.IRelateToAnotherClass).WithMany(p => p.IGetRelatedToByAnotherClasses).HasForeignKey(p => p.Id);
If I understand what you're asking correctly, you'd want both. You want an int FK property and an object property to use as the navigation property.
The end result would look something like this:
public class Employee
{
[Key]
public int EmployeeID { get; set; }
[ForeignKey("Store")]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual Store Store { get; set; }
}
public class Store
{
[Key]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual List<Employee> Employees { get; set; }
}
If you haven't already, take a look at navigation properties and lazy-loading. Note that EF is clever enough to figure out that an int StoreID property corresponds to an object Store property, but if they are named differently (such as without the ID suffix), you must use the [ForeignKey] annotation.