Database Generated by EF5 Not Creating Join Table When There are Multiple Relationships - entity-framework

I have a User and an Organization class. They look like this
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Organization> Organizations { get; set; }
}
public class Organization : EntityBase
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<User> Users { get; set; }
}
And both inherit from an EntityBase class to get common fields like Id and created/updated tracking.
public abstract class EntityBase
{
public int Id { get; set; }
public DateTime Created { get; set; }
public virtual User CreatedBy { get; set; }
public DateTime Updated { get; set; }
public virtual User UpdatedBy { get; set; }
}
As denoted by the ICollection properties on both, there should be a many-to-many relation. However when my database is autogenerated I get incorrect foreign keys added to my tables
If I change the CreatedBy and UpdatedBy to be strings instead of User properties I get a join table, which is what I was looking for.
Is this a matter of Entity Framework simply being confused and I need to supply many-to-many configuration in the using fluent mappings, or have I done something wrong?

If you have multiple relationships you need to configure them manually by fluent API or using attributes,
Note:If you have multiple relationships between the same types (for
example, suppose you define the Person and Book classes, where the
Person class contains the ReviewedBooks and AuthoredBooks navigation
properties and the Book class contains the Author and Reviewer
navigation properties) you need to manually configure the
relationships by using Data Annotations or the fluent API
Here is the article from Microsoft.

Related

Table-Per-Type and multiple association using same Foreign Key in Entity Framework CodeFirst

I have the following CodeFirst Classes. I am trying to model a TPT structure here. An user can login and access his Individual profile or his many company profiles. Individual and Company have polymorphic association with other tables.
public class LoginDetail
{
public int Id { get; set; }
//Other Properties
public virtual Individual Individual { get; set; }
public virtual ICollection<Company> Companies{ get; set; }
}
public abstract class Profile
{
public int Id { get; set; }
//Other Properties
public virtual int LoginId { get; set; }
public virtual LoginDetail Login{ get; set; }
}
[Table("Individuals")]
public class Individual : Profile
{
//Other Properties
}
[Table("Companies")]
public class Company: Profile
{
//Other Properties
}
This maps to LoginDetails, Profiles, Individuals and Companies Table. Everything works fine but in order to associate one-to-many relation between LoginDeatils and Companies it creates an extra LoginDetail_Id in Companies Table. This breaks other parts of my model. How do i tell entity framework to use LoginId in Profiles Table for both the one-to-one relation between LoginDeatils and Individuals and one-to-may relation between LoginDeatils and Companies
you should use InverseProperty and ForeignKey attribute ,
try this code :
public class LoginDetail
{
public int Id { get; set; }
//Other Properties
public virtual Individual Individual { get; set; }
[InverseProperty("Login")]
public virtual ICollection<Profile> Profiles{ get; set; }
}
public abstract class Profile
{
public int Id { get; set; }
//Other Properties
public virtual int LoginId { get; set; }
[InverseProperty("Profiles")]
[ForeignKey("LoginId")]
public virtual LoginDetail Login{ get; set; }
}

Advanced TPH Mapping to Legacy Database

I have been working on a project in which I am trying to mold entity framework to an existing FoxPro 2.x database in order to use the data while leaving the tables readable to a legacy application (more details on my previous question).
I've had pretty good luck configuring the DBContext to the physical data tables and I have most of my mapping set up. The legacy data structure has a Bills table with a unique primary Id key, but all the LineItems that can be posted to a bill are stored in a single Charges table without a simple primary key.
My question pertains to discriminator mapping in code-first EF. I am recreating the table as TPH in my data objects, so I have
public abstract class Posting
{
public System.DateTime? Post_Date { get; set; }
public string Bill_Num { get; set; }
public string Type { get; set; }
public string Pcode { get; set; }
public string Pdesc { get; set; }
public decimal? Custid { get; set; }
public string Createby { get; set; }
public System.DateTime? Createdt { get; set; }
public string Createtm { get; set; }
public string Modifyby { get; set; }
public System.DateTime? Modifydt { get; set; }
public string Modifytm { get; set; }
public string Linenote { get; set; }
public decimal? Version { get; set; }
public string Id { get; set; }
public string Batch { get; set; }
public virtual Billing Bill { get; set; }
}
public abstract class Charge : Posting
{
}
public class ServiceLine : Charge
{
public string Chargeid { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
public class ChargeVoid : Charge
{
}
public abstract class Payment : Posting
{
}
public class PaymentLine : Payment
{
public string Postid { get; set; }
public string Svc_Code { get; set; }
public string Name { get; set; }
public string Checkno { get; set; }
public System.DateTime? Checkdate { get; set; }
}
public class PaymentVoid : Payment
{
}
where my mapping strategy so far is along these lines:
public class PostingMap : EntityTypeConfiguration<Posting>
{
public PostingMap()
{
// Primary Key
this.HasKey(t => new {t.Bill_Num, t.Post_Date, t.Pcode});
this.Map<Charge>(m => m.Requires("Type").HasValue("C"))
.ToTable("Charges");
this.Map<Payment>(m => m.Requires("Type").HasValue("P"))
.ToTable("Charges");
}
}
I have omitted some fields and mapping classes, but this is the core of it.
Every record has the C/P classification, so this makes everything in the table either a Charge or a Payment.
Every Posting is associated with a Bill via Bill_Num foreign key.
The ServiceLine object is only distinct from ChargeVoid objects (which are adjustment entries and no-value information entries associated with a bill) by having values for Pcode and Chargeid (which is just Bill_Num tagged with 01++). I have no idea how to model this.
It is very similar for the Payment hierarchy as well.
So with my current setup, I have Postings which doesn't have a unique key, Charges which has a subset of ServiceLines with values for Chargeid and Pcode and a subset with nulls, and Payments similar to Charges. PaymentLines are also many-to-one with ServiceLines by way of Pcode while PaymentVoids have Pcode = null.
Is there a way I can assign this complex mapping since I can't simply discriminate on !null? On top of that, will EF handle the key assignments once I get the inheritance set up, or am I going to have issues there as well?
Also, if there is a better way to break this object inheritance down, I am all ears.

EF5, Inherited FK and cardinality

I have this class structure:
public class Activity
{
[Key]
public long ActivityId { get; set; }
public string ActivityName { get; set; }
public virtual HashSet<ActivityLogMessage> ActivityLogMessages { get; set; }
public virtual HashSet<FileImportLogMessage> FileImportLogMessages { get; set; }
public virtual HashSet<RowImportLogMessage> RowImportLogMessages { get; set; }
}
public abstract class LogMessage
{
[Required]
public string Message { get; set; }
public DateTimeOffset CreateDate { get; set; }
[Required]
public long ActivityId { get; set; }
public virtual Activity Activity { get; set; }
}
public class ActivityLogMessage : LogMessage
{
public long ActivityLogMessageId { get; set; }
}
public class FileImportLogMessage : ActivityLogMessage
{
public long? StageFileId { get; set; }
}
public class RowImportLogMessage : FileImportLogMessage
{
public long? StageFileRowId { get; set; }
}
Which gives me this, model
Each Message (Activity, File or Row) must have be associated with an Activity. Why does the 2nd and 3rd level not have the same cardinality as ActivityLogMessage ? My attempts at describing the foreign key relationship (fluent via modelbuilder) have also failed.
This is really an academic exercise for me to really understand how EF is mapping to relational, and this confuses me.
Regards,
Richard
EF infers a pair of navigation properties Activity.ActivityLogMessages and ActivityLogMessage.Activity with a foreign key property ActivityLogMessage.ActivityId which is not nullable, hence the relationships is defined as required.
The other two relationships are infered from the collections Activity.FileImportLogMessages and Activity.RowImportLogMessages. They neither have an inverse navigation property on the other side nor a foreign key property which will - by default - lead to optional relationships.
You possibly expect that LogMessage.Activity and LogMessage.ActivityId is used as inverse property for all three collections. But it does not work this way. EF cannot use the same navigation property in multiple relationships. Also your current model means that RowImportLogMessage for example has three relationships to Activity, not only one.
I believe you would be closer to what you want if you remove the collections:
public virtual HashSet<FileImportLogMessage> FileImportLogMessages { get; set; }
public virtual HashSet<RowImportLogMessage> RowImportLogMessages { get; set; }
You can still filter the remaining ActivityLogMessages by the derived types (for example in not mapped properties that have only a getter):
var fileImportLogMessages = ActivityLogMessages.OfType<FileImportLogMessage>();
// fileImportLogMessages will also contain entities of type RowImportLogMessage
var rowImportLogMessage = ActivityLogMessages.OfType<RowImportLogMessage>();

EF 5.0 code first navigation property

I have got a User entity there are my users are stored in. For some users (admins) I would like to add additional details.
I have written following code.
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
[ForeignKey("AdminDetailID")]
public virtual AdminDetail AdminDetail { get; set; }
public int? AdminDetailID { get; set; }
}
public class AdminDetail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AdminDetailID { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }
public int UserId { get; set; }
}
I like to navigate from my AdminDetail table back to my User Profile table by writing eg. admin.UserProfile.UserName. However, when I run Database-Update I receive:
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
When I delete the UserProfile property everything works great.. How can I create a "back" navigation within my AdminDetail class?
Entity Framework Code-First allows for polymorphic classes to be stored in the same table. Have you considered using a relationship like this instead?
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
}
public class AdminProfile : UserProfile
{
// AdminProfile members.
}
This results in a UserProfile table with an additional column called Discriminator that EF creates and manages for you. This column indicates whether each row in the table is a UserProfile or an AdminProfile. Rows which are of type UserProfile ignore the columns that are specific to AdminProfile when accessed by EF.
Entity framework handles all of the type discrimination for you so you don't need to worry about that directly. Your DbContext will simply have a DbSet which can also store entities of type AdminProfile.
You don't need to have a FK in your UserProfile class. To set up a proper 1:1 only the AdminDetail class would actually need to have the foreign key to the UserProfile class. You can still keep the virtual property to be able to navigate back and forth, and EF will know what it is that you're doing. Similar to this:
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
public virtual AdminDetail AdminDetail { get; set; }
}

How should I model one-to-many relation with EntityFramework 4 to work with migration and ASP MVC

I'm trying to use ASP MVC 4 and Entity Framework 4 to create pretty simple web site.
I need to use the migration feature because I will deploy the application to shared hosting (GoDaddy) and I don't want to manually change tables on each change.
What is the correct way to model one-to-many relations? Using the other entity type or the other entity's primary key type?
When I use the other entity type, which is preferred because it keeps the model cleaner, the migration tools worked but the scaffolding of ASP MVC did not. Even when I've manually add drop down to select the other entity ASP MVC did not parse the request right and did not set the other entity property.
This is the two options:
Option1: Use other entity type.
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
public Tenant Tenant { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
}
Option 2: use primary key type.
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
public string TenantID { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
}
I've create MVC controller with scaffolding for the Survey entity in my ASP MVC 4 project. It create the CRUD controller and views. In the view it did not put any field for the Tenant.
After I've add it myself the method Create(Tenant tenant) was called but the Tenant field that was sent by the HTML form did not get parsed by MVC and did not set the Tenant field of the Survey entity.
Ido
These look like you are mapping one-to-one relationships and not one-to-many. If one Survey can have multiple Tenants then:
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
public virtual Survey Survey {get; set;}
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
public virtual ICollection<Tenant> Tenant {get; set;}
}
I've found this series of posts which explain how to make EF models so that they will work with both EF and ASP MVC.
The idea is to have both "plain" reference type and strong reference type.
public class Team
{
public int TeamId { get; set; }
// ... other Team properties go here
// Each Team has an optional "next opponent" which is another Team
public int? NextOpponentId { get; set; }
[ForeignKey("NextOpponentId")] public virtual Team NextOpponent { get; set; }
// Each Team also has a required Manager and Administrator, both of which are people
public int ManagerId { get; set; }
public int AdministratorId { get; set; }
[ForeignKey("ManagerId")] public virtual Person Manager { get; set; }
[ForeignKey("AdministratorId")] public virtual Person Administrator { get; set; }
}