I'm new to code first and derived from DB Context. Here is a excerpt of my Model.
[Table("pm_Material")]
public class Material
{
public Material()
{
this.ProductionStepLogs = new HashSet<ProductionStepLog>();
}
[Key]
public int MaterialId { get; set; }
public int MaterialTypeId { get; set; }
public string Description { get; set; }
public decimal CostRate { get; set; }
public virtual MaterialType MaterialType { get; set; }
public virtual ICollection<ProductionStepLog> ProductionStepLogs { get; set; }
}
[Table("pm_ProductionStepLog")]
public class ProductionStepLog
{
public ProductionStepLog()
{
this.Materials = new HashSet<Material>();
}
[Key]
public System.Guid ProductionStepLogId { get; set; }
public int ProductionStepId { get; set; }
public System.Guid ProductId { get; set; }
public Nullable<System.DateTime> BeginStep { get; set; }
public Nullable<System.DateTime> EndStep { get; set; }
public int UserId { get; set; }
public virtual Product Product { get; set; }
public virtual ProductionStep ProductionStep { get; set; }
public virtual ICollection<Material> Materials { get; set; }
}
The DB creation works fine, but I want to specify the name of the auto-generated many-to-many table "ProductionStepLogMaterials" using [Table("pm_ProductionStepLogMaterials")].
Is this possible?
You should override your protected override void OnModelCreating(DbModelBuilder modelBuilder) of your own DBContext class like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Material>()
.HasMany(a => a.ProductionStepLog)
.WithMany(a => a.Material)
.Map(x =>
{
x.ToTable("NameOfYourTable");
x.MapLeftKey("MaterialId");
x.MapRightKey("ProductionStepLogId");
});
}
AFAIK, this is impossible with Data Annotations, but it is possible with configuration fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<E1Type>()
.HasMany(e1 => e1.Collection)
.WithMany(e2 => e2.Collection)
.Map(config => config.ToTable("MyTable"));
}
Related
I have a project that is running with .net core 6, EF Core 6.0.9, DB -> postgresql 14.
I have these classes
public class Language
{
[Key]
public long Id { get; set; }
public string PublicId { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> CoursesFrom { get; set; }
public virtual ICollection<Course> CoursesTo { get; set; }
}
public class Course
{
[Key]
public long Id { get; set; }
public string PublicId { get; set; }
public string Name { get; set; }
public virtual Language LanguageFrom { get; set; }
public virtual Language LanguageTo { get; set; }
}
The relation is defined as follows:
public class LanguageConfiguration : IEntityTypeConfiguration<Language>
{
public void Configure(EntityTypeBuilder<Language> builder)
{
...
builder.HasMany(l => l.CoursesFrom)
.WithOne(c => c.LanguageFrom);
builder.HasMany(l => l.CoursesTo)
.WithOne(c => c.LanguageTo);
}
}
public class CourseConfiguration : IEntityTypeConfiguration<Course>
{
public void Configure(EntityTypeBuilder<Course> builder)
{
...
builder.HasOne(l => l.LanguageFrom)
.WithMany(c => c.CoursesFrom)
.OnDelete(DeleteBehavior.SetNull);
builder.HasOne(l => l.LanguageTo)
.WithMany(c => c.CoursesTo)
.OnDelete(DeleteBehavior.SetNull);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new LanguageConfiguration());
modelBuilder.ApplyConfiguration(new CourseConfiguration());
}
And LINQ expression
var query = Context.Course
.Include(l => l.LanguageFrom)
.Include(l => l.LanguageTo)
.ToList();
Main entity is returned but fields LanguageFrom and LanguageTo are null - the Include() does nothing. What am I doing wrong?
When there are multiple navigation properties defined between two types shadow navigation properties do not work. You should define two fields to serve as foreign key in the Course entity:
public class Course
{
[Key]
public long Id { get; set; }
public string PublicId { get; set; }
public string Name { get; set; }
public long LanguageFromId { get; set; }
public long LanguageToId { get; set; }
public virtual Language LanguageFrom { get; set; }
public virtual Language LanguageTo { get; set; }
}
Then
public class CourseConfiguration : IEntityTypeConfiguration<Course>
{
public void Configure(EntityTypeBuilder<Course> builder)
{
...
builder.HasOne(l => l.LanguageFrom)
.WithMany(c => c.CoursesFrom)
.HasForeignKey("LanguageFromId")
.OnDelete(DeleteBehavior.SetNull);
builder.HasOne(l => l.LanguageTo)
.WithMany(c => c.CoursesTo)
.HasForeignKey("LanguageToId")
.OnDelete(DeleteBehavior.SetNull);
}
}
Returning the error message above in a new asp.net core 3.1 project. I have a many-to-many relationship configured according to Text
Not exactly sure why this would be happening but I have added the EntityFrameworkCore package to the project so I'm not sure why it's returning this error.
DBContext.cs:
using Microsoft.EntityFrameworkCore;
using Ballista.Models;
namespace Ballista.Data
{
public class AnnouncementsContext : DbContext
{
public AnnouncementsContext (DbContextOptions<AnnouncementsContext> options)
: base(options)
{
}
public DbSet<Announcements> Announcements { get; set; }
public DbSet<AnnouncementTargets> AnnouncementTargets { get; set; }
public DbSet<TargetGroup> TargetGroup { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AnnouncementTargets>()
.HasKey(at => new { at.AnnouncementID, at.TargetGroupID });
modelBuilder.Entity<AnnouncementTargets>()
.HasOne(at => at.Announcement)
.WithMany(a => a.AnnouncementTargets)
.HasForeignKey(at => at.AnnouncementID);
modelBuilder.Entity<AnnouncementTargets>()
.HasOne(at => at.TargetGroup)
.WithMany(tg => tg.AnnouncementTargets)
.HasForeignKey(t => t.TargetGroupID);
base.OnModelCreating(modelBuilder);
}
}
AnnouncementModels.cs:
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace Ballista.Models
{
public class Announcements
{
public int ID { get; set; }
public string Title { get; set; }
public string SubTitle { get; set; }
public string Content { get; set; }
public ICollection<AnnouncementTargets> AnnouncementTargets { get; set; }
}
public class TargetGroup
{
public int ID { get; set; }
public string Group { get; set; }
public ICollection<AnnouncementTargets> AnnouncementTargets { get; set; }
}
public class AnnouncementTargets
{
public int AnnouncementID { get; set; }
public Announcements Announcement { get; set; }
public int TargetGroupID { get; set; }
public TargetGroup TargetGroup { get; set; }
}
}
I have a many-to-many relationship configured but I keep getting the error message <invalid-global-code>.OnModelCreating(ModelBuilder)': no suitable method found to override
DbContext have OnModelCreating(DbModelBuilder) to override.
Change definition of function from OnModelCreating(ModelBuilder modelBuilder) to OnModelCreating(DbModelBuilder modelBuilder)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
I'm trying to do one-one relationship for MVC5 codefirst. I've looked this page and did exactly same things but I've got an error.
Here is my classes and context:
Product:
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
/// <summary>
/// Display order
/// </summary>
public int Order { get; set; }
public string TitleBackgroundColor { get; set; }
public virtual TblClass TblClass { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Price> Prices { get; set; }
public virtual ICollection<ProductFeature> ProductFeatures { get; set; }
}
TblClass:
public class TblClass
{
[Key, ForeignKey("Product")]
public int ProductId { get; set; }
public string ClassName { get; set; }
public virtual ICollection<Permission> Permissions { get; set; }
public virtual Product Product { get; set; }
public int ClassOrder { get; set; }
}
DBContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Role>()
.HasMany<UserProfile>(r => r.UserProfiles)
.WithMany(u => u.Roles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("RoleId");
m.MapRightKey("UserId");
});
modelBuilder.Entity<TblClass>()
.HasKey(c => c.ProductId);
modelBuilder.Entity<Product>()
.HasOptional(f => f.TblClass)
.WithRequired(s => s.Product)
.Map(t => t.MapKey("ProductId"));
}
And when I try to run 'update-database -verbose' I've got this error:
The navigation property 'Product' declared on type 'YazililarGaranti.Domain.Entities.TblClass' has been configured with conflicting foreign keys.
You do not have to use .Map(t => t.MapKey("ProductId") when making an 1:1 relationship. This should work:
public class Product
{
public int ProductId { get; set; }
//... other properties
public virtual TblClass TblClass { get; set; }
//... other properties
}
public class TblClass
{
//[Key, ForeignKey("Product")] <-- remove these attributes
public int ProductId { get; set; }
public string ClassName { get; set; }
//.. other properties
public virtual Product Product { get; set; }
public int ClassOrder { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//other mappings
modelBuilder.Entity<TblClass>()
.HasKey(c => c.ProductId);
modelBuilder.Entity<Product>()
.HasKey(c => c.ProductId); //consider to use database generated option
//modelBuilder.Entity<Product>().Property(t => t.ProductId)
//.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Product>()
.HasOptional(f => f.TblClass)
.WithRequired(s => s.Product);
}
Hope this helps!
My "ShoppingCart" and "ShoppingCartItems" tables are already in my database. I am trying to add a new table called "discountCodes". Each shoppingCart can have one or zero discountCodes.
The error I am receiving is: Invalid column name 'discountId'.
[Table("ShoppingCarts")]
public class ShoppingCart
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Column("cartID")]
public string cartID { get; set; }
public virtual IList<ShoppingCartItem> CartItems { get; set; }
[Column("dateCreated")]
public DateTime? DateCreated { get; set; }
[Column("userID")]
public Guid UserID { get; set; }
public int? discountId { get; set; }
public virtual Discount discount { get; set; }
}
[Table("discountCodes")]
public class Discount
{
public int discountId { get; set; }
public string discountCode{get;set;}
[Required]
public int percentOff { get; set; }
[Required]
public Boolean isActive { get; set; }
public ShoppingCart ShoppingCart { get; set; }
}
public class ShoppingCartContext : DbContext
{
public ShoppingCartContext()
: base("MYDBConnectionString")
{
Database.SetInitializer<ShoppingCartContext>(new CreateDatabaseIfNotExists<ShoppingCartContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ShoppingCart>().HasKey(t => t.cartID)
.HasOptional(t => t.discount)
.WithOptionalPrincipal(d => d.ShoppingCart)
.Map(t => t.MapKey("cartID"));
modelBuilder.Entity<Discount>().HasKey(t => t.discountId)
.HasOptional(q => q.ShoppingCart);
}
public DbSet<Discount> discountCodes { get; set; }
public DbSet<ShoppingCart> ShoppingCart { get; set; }
public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
}
If you are working on an existing database you have to implement a DbMigration like it's explain here: Code First Migrations.
If you are in development phase, the easiest way is to drop the database.
I have my main class:
Person
and some Additional classes:
AddressInfo and DocumentInfo.
How do I configure my relationship so it will work like Person with 2 addresses and 3 documents?
It's not arrays. It's named links.
Looks like:
public class Person
{
public int Id {get;set;}
public virtual AddressInfo RegistrationAddress {get;set;}
public virtual AddressInfo ResidenceAddress {get;set;}
}
public class AdressInfo
{
public int Id {get;set;}
public virtual Person Person {get;set;}
}
Same with DocumentInfo.
It can be not even close to right solution.
Try this. It work for me. I did not do the attribute but the code first api works better for me
namespace OneToOne
{
class Program
{
static void Main(string[] args)
{
using (var db = new MyContext())
{
var person = new Person();
db.Persons.Add(person);
db.SaveChanges();
person = db.Persons.Include("RegistrationAddress").Include("ResidenceAddress").FirstOrDefault();
var address = new AdressInfo { Person = person };
person.RegistrationAddress = address;
person.ResidenceAddress = address;
db.Persons.Attach(person);
db.SaveChanges();
}
}
}
public class MyContext : DbContext
{
public DbSet Persons { get; set; }
public DbSet AdressInfos { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new AdressInfoConfig());
modelBuilder.Configurations.Add(new PersonConfig());
}
}
public class PersonConfig : EntityTypeConfiguration
{
public PersonConfig()
{
this.ToTable("Persons");
this.HasKey(x => x.Id).Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class Person
{
public int Id { get; set; }
public virtual AdressInfo RegistrationAddress { get; set; }
public virtual AdressInfo ResidenceAddress { get; set; }
}
public class AdressInfo
{
public int Id { get; set; }
public virtual Person Person { get; set; }
}
public class AdressInfoConfig : EntityTypeConfiguration
{
public AdressInfoConfig()
{
this.ToTable("AdressInfos");
this.HasKey(x => x.Id).Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.RegistrationAddress).WillCascadeOnDelete(false);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.ResidenceAddress).WillCascadeOnDelete(false);
}
}
}
Tried Your solution.
Thats my models:
`
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public string IdentificationNumber { get; set; }
public string HomePhone { get; set; }
public string MobilePhone { get; set; }
public string Email { get; set; }
public bool DeadHead { get; set; }
public virtual PassportInfo Passport { get; set; }
public virtual PassportInfo DriverLicense { get; set; }
public virtual PassportInfo PensionCertificate { get; set; }
public virtual AddressInfo ResidenseAddress { get; set; }
public virtual AddressInfo RegistrationAddress { get; set; }
}
public class PassportInfo
{
public int Id { get; set; }
public string Series { get; set; }
public int Number { get; set; }
public string IssuedBy { get; set; }
public DateTime IssueDate { get; set; }
public virtual Person Person { get; set; }
}
public class AddressInfo
{
public int Id { get; set; }
public string Index { get; set; }
public string Region { get; set; }
public string District { get; set; }
public string Street { get; set; }
public string Locality { get; set; }
public int House { get; set; }
public int Apartment { get; set; }
public int? StreetTypeId { get; set; }
public StreetType StreetType { get; set; }
public int? LocalityTypeId { get; set; }
public LocalityType LocalityType { get; set; }
public virtual Person Person { get; set; }
}
`
And thats my context config:
public class InsuranceContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<AddressInfo> AddressInfos { get; set; }
public DbSet<PassportInfo> PassportInfos { get; set; }
public DbSet<LocalityType> LocalityTypes { get; set; }
public DbSet<StreetType> StreetTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new AddressInfoConfig());
modelBuilder.Configurations.Add(new PersonConfig());
modelBuilder.Configurations.Add(new PassportInfoConfig());
}
}
public class PersonConfig : EntityTypeConfiguration<Person>
{
public PersonConfig()
{
this.ToTable("People");
this.HasKey(x => x.Id)
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class AddressInfoConfig : EntityTypeConfiguration<AddressInfo>
{
public AddressInfoConfig()
{
this.ToTable("AddressInfos");
this.HasKey(x => x.Id).Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.RegistrationAddress).WillCascadeOnDelete(false);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.ResidenseAddress).WillCascadeOnDelete(false);
}
}
public class PassportInfoConfig : EntityTypeConfiguration<PassportInfo>
{
public PassportInfoConfig()
{
this.ToTable("PassportInfos");
this.HasKey(x => x.Id).Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.Passport).WillCascadeOnDelete(false);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.DriverLicense).WillCascadeOnDelete(false);
this.HasOptional(x => x.Person).WithOptionalDependent(x => x.PensionCertificate).WillCascadeOnDelete(false);
}
}
Btw it throws exception "Either the parameter #objname is ambiguous or the claimed #objtype (COLUMN) is wrong." when i try to use context or update a base with migration.