EF6 1:optional + 1:many - c#

got a little Problem with EF6 Code First (in a MVC Web App).
Enum to classify an Account in a "AccountCircle":
public enum AccountType
{
Circle1,
Circle2,
Circle3,
Circle4,
Circle5
}
Main class for Accounts:
[Table("Accounts")]
public class AccountModel
{
public AccountModel()
{
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public string Name { get; set; }
public string EMail { get; set; }
}
The main Company-Model
[Table("Companys")]
public class CompanyModel
{
public CompanyModel()
{
this.AccountCircle = new AccountCircleModel();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public string Name { get; set; }
public int? idAccountCircle { get; set; }
public AccountCircleModel AccountCircle { get; set; }
}
Class for a single circle:
[Table("AccountCircles")]
public class AccountCircleModel
{
public AccountCircleModel()
{
this.Member = new List<AccountCirleMemberModel>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public int idCompany { get; set; }
public CompanyModel Company { get; set; }
public List<AccountCirleMemberModel> Member { get; set; }
}
and last but not least the account itself with an additinal information what type of member:
[Table("AccountCircleMember")]
public class AccountCirleMemberModel
{
public AccountCirleMemberModel()
{
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public AccountType Typ { get; set; }
public int idAccount { get; set; }
public virtual AccountModel Account { get; set; }
public int idAccountCircle { get; set; }
public AccountCircleModel AccountCircle { get; set; }
}
And the DbContext
public class TestContext : DbContext
{
public TestContext()
: base()
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// modelBuilder Infos.....
base.OnModelCreating(modelBuilder);
}
#region Tables
public DbSet<AccountModel> Accounts { get; set; }
public DbSet<CompanyModel> Companys { get; set; }
public DbSet<AccountCircleModel> AccountCircles { get; set; }
#endregion
}
So there is a Company, which has an optional property of type "AccountCircle" (1:optional)
In the Accountcircle, there is a List of Accounts with a separate enum (AccountCirleMemberModel 1:many)
I tried hundred of modelBuilder methods to give the EF6 the necessary infos, but no success.
Has someone a hint, to give the DbModelBuilder in the "protected override void OnModelCreating" method the correct relations data?
Big thanks in advance!
monte

Not sure if this answers your question, but if you want to specify the relationships between tables in the DB, using EF Code First, you have to use modifier virtual for your "navigation" properties - those that map to another table. So the code would look like:
[Table("Companys")]
public class CompanyModel
{
// other properties and the rest of the code here
public virtual AccountCircleModel AccountCircle { get; set; }
}
[Table("AccountCircles")]
public class AccountCircleModel
{
// other properties and the rest of the code here
public virtual CompanyModel Company { get; set; }
public virtual ICollection<AccountCirleMemberModel> Member { get; set; }
}
[Table("AccountCircleMember")]
public class AccountCirleMemberModel
{
// other properties and the rest of the code here
public virtual AccountModel Account { get; set; }
public virtual AccountCircleModel AccountCircle { get; set; }
}
You don't need to add properties that would serve as foreign keys - EF would take care of that. You could specify them, but then you'd have to use fluent API to map those properties as foreign keys for specific tables.

Related

Error on Entity framework insert field not found

I got Error on Entity framework insert. When I want to add records in sub table I got field not found error.
This is my main table class as below:
public class Rcp
{
public Rcp()
{
RcpFl = new HashSet<RcpFiles>();
}
[Column("Rcp_ID")]
public int RcpId { get; set; }
public String RcpTitle { get; set; }
public virtual ICollection<RcpFiles> RcpFl { get; set; }
}
And this is my sub table class:
public class RcpFiles
{
[Key]
public int RcpFile_Id { get; set; }
public byte[] Photo { get; set; }
public virtual Rcp rcp { get; set; }
}
On insert time I got error Field "RcpFile_Id" could not be found.
You are relying on convention regarding property names. This is how you can specify the relationships using annotations.
public class Rcp
{
public Rcp()
{
RcpFl = new HashSet<RcpFiles>();
}
[Key]
[Column("Rcp_ID")]
public int RcpId { get; set; }
public String RcpTitle { get; set; }
[InverseProperty(nameof(RcpFiles.rcp))]
public virtual ICollection<RcpFiles> RcpFl { get; set; }
}
public class RcpFiles
{
[Key]
public int RcpFile_Id { get; set; }
public byte[] Photo { get; set; }
[Column("Rcp_ID")]
public int RcpId { get; set; }
[ForeignKey(nameof(RcpId))]
public virtual Rcp rcp { get; set; }
}
It seems you have one-to-many relationship.
First you must add foreign key property to RcpFiles entity:
public class RcpFiles
{
[Key]
public int RcpFile_Id { get; set; }
public byte[] Photo { get; set; }
public virtual Rcp rcp { get; set; }
public int RcpId { get; set; }
}
Then you have to set configuration. You have two options to do that:
Fist is Fluent API:
public class SchoolContext : DbContext
{
public DbSet<RcpFiles> RcpFiles { get; set; }
public DbSet<Rcp> Rcpes{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// configures one-to-many relationship
modelBuilder.Entity<RcpFiles>()
.HasRequired<Rcp>(s => s.rcp)
.WithMany(g => g.RcpFl)
.HasForeignKey<int>(s => s.RcpId);
}
}
The second one is Data annotiation: this part explained by #brian-parker (https://stackoverflow.com/users/1492496/brian-parker)
For more information you can see: https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx

asp.net core identity RoleTable's RoleId as foreign key

This is asp.net core identity role table structure .
AspNetRoles
-------------------
-id (PrimaryKey)
-ConcurrencyStamp
-Name
-NormalizedName
I want to create a new table including this role table's id as foreign key.
Here is my poco for new table
public class NewClass
{
public string Name { get; set; }
public int Type { get; set; }
//
}
How can I reference AspNetRoles's id as foreign key with many-to-one relationship ??
It should be something like this
public class AspNetRoles
{
public int RoleId { get; set; }
public string ConcurrencyStamp { get; set; }
public string Name { get; set; }
public string NormalizedName{ get; set; }
public List<NewClass> NewClasses{ get; set; }
}
public class NewClass
{
public string Name { get; set; }
public int Type { get; set; }
public int RoleId { get; set; }
public AspNetRoles AspNetRole{ get; set; }
}
I am little bit confused which side is your many and which side is one.
If you are using entity framework then just Try with this example.Entity framework is intelligent enough to make one to many relation between them
if your many side is NewClass then try with this.OR if you need multiple role in your NewClass
public class AspNetRole
{
public int RoleId { get; set; }
public string Name { get; set; }
}
public class NewClass
{
..................
................
public ICollection<AspNetRole> AspNetRoles{ get; set; }
}
If your Many side is Roles then try with this
public class AspNetRole
{
public int RoleId { get; set; }
public string Name { get; set; }
public ICollection<NewClass> NewClasses{ get; set; }
}
public class NewClass
{
................
public int AspNetRolesId{ get; set; }
public AspNetRole AspNetRoles{ get; set; }
}
Simply you can do this:
OneToZore:
For One to Zore from AspNetRoles like:
Public class AspNetRole
{
//Other property
public int NewClassId { get; set; }
}
public class NewClass
{
//Other property
public AspNetRole AspNetRole { get; set; }
}
For One to Zore from NewClass:
Public class AspNetRole
{
//Other property
public NewClass NewClass { get; set; }
}
public class NewClass
{
//Other property
public int AspNetRoleId { get; set; }
}
OneToMany:
For One to Many from AspNetRole:
Public class AspNetRole
{
//Other property
public int NewClassId { get; set; }
}
public class NewClass
{
//Other property
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
}
For One to Many from NewClass:
Public class AspNetRole
{
//Other property
public virtual ICollection<NewClass> NewClasses { get; set; }
}
public class NewClass
{
//Other property
public int AspNetRoleId { get; set; }
}
ManyToMany:
For this issue you can not create this as directly, you have to find a table that has relation with that table. For example User table has a relation(One to Many) with AspNetRole like:
Public class AspNetRole
{
//Other property
public int UserId { get; set; }
}
public class User
{
//Other property
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
}
Now If you have relation as OneToMany with User table, after that you have a relation as ManyToMany with AspNetRole like:
Public class AspNetRole
{
//Other property
public int UserId { get; set; }
}
public class User
{
//Other property
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
public virtual ICollection<NewClass> NewClasses { get; set; }
}
public class NewClass
{
//Other property
public int AspNetRoleId { get; set; }
}

Table per Type in Entity Framework Core 2.0

These are my models:
public class Company
{
public int CompanyId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public string Url { get; set; }
//...
}
public class HeadOffice : Company
{
public int HeadOfficeId { get; set; }
public virtual List<BranchOffice> BranchOffice { get; set; } = new List<BranchOffice>();
}
public class BranchOffice : Company
{
public int BranchOfficeId { get; set; }
public virtual HeadOffice HeadOffice { get; set; }
}
I wanted the following database structure:
Table Company
CompanyId (PK)
Name
Address
Email
Url
Table HeadOffice
HeadOfficeId (PK)
CompanyId (FK)
Table BranchOffice
BranchOfficeId (PK)
HeadOfficeId (FK)
CompanyId (FK)
How can I do this?
When I create this migration, the EF creates just one table, with all columns! I don't want this approach!
You would have to change your Model to look like this, note that you can't use inheritance using this approach:
public class Company
{
public int CompanyId { get; set; }
//...
}
public class Company
{
public int CompanyId { get; set; }
public string Name { get; set; }
//...
}
public class HeadOffice
{
[ForeignKey(nameof(Company))]
public int CompanyId { get; set; }
public Company Company { get; set; }
// Add Properties here
}
public class BranchOffice
{
[ForeignKey(nameof(Company))]
public int CompanyId { get; set; }
public Company Company { get; set; }
// Add Properties here
}
Your DbContext:
public class YourContext : DbContext
{
public DbSet<Company> Companys { get; set; }
public DbSet<HeadOffice> HeadOffices { get; set; }
public DbSet<BranchOffice> BranchOffices { get; set; }
public YourContext(DbContextOptions<YourContext> options)
: base(options)
{
}
}
You could then use EF Core Migrations. The command would look somewhat like this:
dotnet ef migrations add Initial_TPT_Migration -p ./../../ModelProject.csproj -s ./../../ModelProject.csproj -c YourContext -o ./TptModel/CodeFirst/Migrations
It generats a Class Initial_TPT_Migration that contains methods to generate your database.
Usage
To query you would need to map Company Properties to the fieldnames. If you combine this with the Repository Pattern (link), it could actually be as convenient as the default approach in EF Core currently is to use.
YourContext ctx = ...
// Fetch all BranchOffices
var branchOffices = ctx.BranchOffices
.Select(c => new BranchOffice()
{
CompanyId = c.CompanyId,
Name = c.Company.Name,
})
.ToList();
You can find more informations about this approach here.
You can find an answer here https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/inheritance?view=aspnetcore-2.1
Also check this topic out if you need many inherited classes against one table
https://learn.microsoft.com/en-us/ef/core/modeling/relational/inheritance
Copying the code here just in case microsoft used to mess with urls and docs
Each inherited type per table
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder b)
{
b.Entity<Student>().ToTable("Student");
b.Entity<Instructor>().ToTable("Instructor");
b.Entity<Person>().ToTable("Person");
}
}
public abstract class Person
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
}
public class Instructor : Person
{
public DateTime HireDate { get; set; }
}
public class Student : Person
{
public DateTime EnrollmentDate { get; set; }
}
Many inherited types in one table
public class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasDiscriminator<string>("blog_type")
.HasValue<Blog>("blog_base")
.HasValue<RssBlog>("blog_rss");
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}

How can I configure many-to-many relationship to my own created entity in entity framework

I have some classes:
public class Values : Entity
{
[Key]
public int Values_ID { get; set; }
[Required]
public string Values_Name { get; set; }
[Required]
public int ValuesNumeric { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
}
public class GQMetric : Entity
{
[Key]
public int GQMetric_ID { get; set; }
[Required]
public string GQMetricName { get; set; }
[Required]
public int Importance_ID { get; set; }
public virtual List<GQMetricsQuestions> GQMetricsQuestions { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
public virtual ImportanceScale ImportanceScale { get; set; }
}
I need to create many-to-many relationship to my own created class ValuesMetrics, not to automatically generated table by entity framework. I have tried a lot of solutions here, here and here but none of it did not work. Eventually, I did this:
public class ValuesMetrics : Entity
{
public int GQMetric_ID { get; set; }
public int Value_ID { get; set; }
public virtual GQMetric GQMetric { get; set; }
public virtual Values Values { get; set; }
}
FluentAPI:
modelBuilder.Entity<ValuesMetrics>()
.HasKey(c => new { c.GQMetric_ID, c.Value_ID });
modelBuilder.Entity<GQMetricsQuestions>()
.HasKey(c => new { c.GQMetric_ID, c.Question_ID });
but created table (ValuesMetrics) have an excessive relationship (GQMetrics_GQMetric_ID). I need only two primary keys from Values and GQMetrics tables
Can you advice me how to solve this problem? Thanks for any help!
Applied #Esteban 's solution from the link already referenced by you: Create code first, many to many, with additional fields in association table
Basically I did the following three changes:
Used POCO entities instead of inheriting from Entity class
Removed EF attributes, since we'll be using fluent API anyway
Changed fluent API configuration
Resulting code:
public class MyContext : DbContext
{
public DbSet<Values> Values { get; set; }
public DbSet<GQMetric> GqMetric { get; set; }
public DbSet<ValuesMetrics> ValuesMetrics { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Values>().HasKey(values => values.Values_ID);
modelBuilder.Entity<GQMetric>().HasKey(metric => metric.GQMetric_ID);
modelBuilder
.Entity<ValuesMetrics>()
.HasKey(valuesMetrics => new
{
valuesMetrics.Value_ID,
valuesMetrics.GQMetric_ID
});
modelBuilder
.Entity<ValuesMetrics>()
.HasRequired(valuesMetrics => valuesMetrics.Values)
.WithMany(valueMetrics => valueMetrics.ValuesMetrics)
.HasForeignKey(valueMetrics => valueMetrics.Value_ID);
modelBuilder
.Entity<ValuesMetrics>()
.HasRequired(valuesMetrics => valuesMetrics.GQMetric)
.WithMany(valueMetrics => valueMetrics.ValuesMetrics)
.HasForeignKey(valueMetrics => valueMetrics.GQMetric_ID);
base.OnModelCreating(modelBuilder);
}
}
public class Values
{
public int Values_ID { get; set; }
public string Values_Name { get; set; }
public int ValuesNumeric { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
}
public class GQMetric
{
public int GQMetric_ID { get; set; }
public string GQMetricName { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
}
public class ValuesMetrics
{
public int GQMetric_ID { get; set; }
public int Value_ID { get; set; }
public virtual GQMetric GQMetric { get; set; }
public virtual Values Values { get; set; }
}

EF TPT generating duplicate foreign keys

I am writing an application which uses inheritance and I'm trying to map this to a SQL Server database with TPT structure.
However, for some reason EF generates duplicate foreign keys in both the superclass and subclass tables.
I have these classes:
public abstract class Answer
{
public int AnswerId { get; set; }
[Required]
[MaxLength(250, ErrorMessage = "The answer cannot contain more than 250 characters")]
public String Text { get; set; }
[Required]
[MaxLength(1500, ErrorMessage = "The description cannot contain more than 1500 characters")]
public String Description { get; set; }
public User User { get; set; }
}
public class AgendaAnswer : Answer
{
[Required]
public AgendaModule AgendaModule { get; set; }
}
public class SolutionAnswer : Answer
{
[Required]
public SolutionModule SolutionModule { get; set; }
}
public abstract class Module
{
// for some reason EF doesn't recognize this as primary key
public int ModuleId { get; set; }
[Required]
public String Question { get; set; }
public String Description { get; set; }
[Required]
public DateTime StartTime { get; set; }
[Required]
public DateTime EndTime { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class AgendaModule : Module
{
public IList<AgendaAnswer> AgendaAnswers { get; set; }
}
public class SolutionModule : Module
{
public IList<SolutionAnswer> SolutionAnswers { get; set; }
}
public class User
{
public int UserId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public int Zip { get; set; }
public bool Active { get; set; }
public virtual IList<AgendaAnswer> AgendaAnswers { get; set; }
public virtual IList<SolutionAnswer> SolutionAnswers { get; set; }
}
And this is the content of my DbContext class:
public DbSet<AgendaModule> AgendaModules { get; set; }
public DbSet<SolutionModule> SolutionModules { get; set; }
public DbSet<AgendaAnswer> AgendaAnswers { get; set; }
public DbSet<SolutionAnswer> SolutionAnswers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Module>().HasKey(m => m.ModuleId);
modelBuilder.Entity<Answer>().HasKey(a => a.AnswerId);
modelBuilder.Entity<AgendaAnswer>().Map(m =>
{
m.ToTable("AgendaAnswers");
});
modelBuilder.Entity<SolutionAnswer>().Map(m =>
{
m.ToTable("SolutionAnswers");
});
}
When I run my application, EF creates the tables how I want them (TPT), but it duplicates the foreign key to users in each of them (see picture).
Thanks in advance
As noted in my comment above, the solution is to remove the AgendaAnswers and SolutionAnswers properties from the User class.
If you want to keep those collections in the User class, you might have to remove the User and UserId properties from the Answer class and instead duplicate them in the AgendaAnswer and SolutionAnswer classes. See this SO question for more information.

Categories

Resources