I'm trying to learn to use Code First. On Picture 1 you can see the EER Model I want to become from my Code First application.
Now I have tried to get the same result from my application. Below you can see the EER Model I've managed to become from my app (with Reverse Engineering in MySQL Workbench).
As you can see I have a problem with creating a one to zero or one relationship between tables 'Properties' and 'Grounds'.
I have an abstract EntityBase class
public abstract class EntityBase
{
public abstract int Id { get; set; }
}
Also a GenericRepository class which inherits the EntityBase class
public class GenericRepository<T> : IRepository<T> where T : EntityBase
A MapDBContext class which inherits the DbContext class. Inside this class you can see that the OnModelCreating method is 'Override'. Inside of that method I have tried to configure the relationship between the 'Properties' and 'Grounds' tables.
public class MapDBContext : DbContext
{
public virtual DbSet<Agreements> Agreements { get; set; }
public virtual DbSet<BuyersRenters> BuyersRenters { get; set; }
public virtual DbSet<Properties> Properties { get; set; }
public virtual DbSet<Grounds> Grounds { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Grounds>().HasOptional(s => s.Properties).WithRequired(lu => lu.Grounds);
base.OnModelCreating(modelBuilder);
}
public MapDBContext(string connectionString) : base(connectionString)
{
}
}
Below are the two Code First classes of the 'Properties' and 'Grounds' tables (NOTE: Properties class is abstract):
[Table("eigendommen")]
public abstract class Properties : EntityBase
{
public override int Id { get; set; }
[Column("gemeente")]
[Required]
public string Town { get; set; }
[Column("straat")]
[Required]
public string Street { get; set; }
public virtual List<Agreements> Agreements { get; set; }
public virtual Grounds Grounds { get; set; }
}
[Table("gronden")]
public class Grounds : Properties
{
[Key]
public override int Id { get; set; }
[Column("opp")]
public double? Surface { get; set; }
[Column("type")]
[EnumDataType(typeof(TypeNames))]
[Required]
public TypeNames Types { get; set; }
public virtual Properties Properties { get; set; }
}
Can somebody help me with what I am doing wrong? I've been searching for hours, tried with the 'required' attribute, with the '?' to make it nullable and with the 'ForeignKey' attribute. But all of these solutions give either errors or a similar table to the one I have now.
To define one to one and zero with code first c#
if you want have one or zero Address for the student.
You can follow the code below
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual StudentAddress StudentAddress { get; set; }
}
public class StudentAddress
{
[ForeignKey("Student")]
public int StudentAddressId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
you must define OnModelCreating in the DbContext and then relation between student and studentaddress.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure the primary key for the StudentAddresses
modelBuilder.Entity<StudentAddress>()
.HasKey(t => t.StudentAddressId);
// Map one-to-zero or one relationship
modelBuilder.Entity<StudentAddress>()
.HasRequired(t => t.Student)
.WithOptional(t => t.StudentAddress);
}
Related
Working with .NET Core 3.0 and EF Core if that impacts things.
I'm trying to define a model where:
A user owns a collection of toys.
A user can have a currently selected toy.
I'm trying to model this as such (BaseModel has common properties to all my entities), and AVUser is my ASP.NET Identity user class.
public abstract class BaseModel
{
public int Id { get; set; }
public DateTime LastModifiedDate { get; set; }
public DateTime CreatedDate { get; set; }
public AVUser CreatedUser { get; set; }
public AVUser ModifiedUser { get; set; }
}
public class Toy: BaseModel
{
[MaxLength(80)]
public string Name { get; set; }
}
public class AVUser : IdentityUser
{
public string FirstName { get; set; }
// The currently selected toy for the user.
public int SelectedToyId { get; set; }
public Toy SelectedToy { get; set; }
}
However, EF Core throws an error stating: Unable to determine the relationship represented by navigation property 'AVUser.Toy' of type 'Toy'.
I am having trouble how I annotate this so it knows that the user can have a collection of toys, and I want to store a single toy with the user as the currently selected one.
Write your model classes as follows:
public class Toy: BaseModel
{
[MaxLength(80)]
public string Name { get; set; }
[ForeignKey("AVUser")]
public string UserId { get; set; }
public AVUser AVUser { get; set; }
}
public class AVUser : IdentityUser
{
public string FirstName { get; set; }
// The currently selected toy for the user.
[ForeignKey("SelectedToy")]
public int SelectedToyId { get; set; }
public Toy SelectedToy { get; set; }
public ICollection<Toy> Toys {get; set;}
}
Then configure in the OnModelCreating in DbContext as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AVUser>().HasOne(a => a.SelectedToy).WithOne();
modelBuilder.Entity<AVUser>().HasMany(a => a.Toys).WithOne(t => t.AVUser).HasForeignKey(t => t.UserId);
}
I have model with defined primary key, but now I need to add inheritance to this class from my abstract class. The problem is, that primary key is required also to abstract class. Names of the PK's properties are different and they have to be different.
Example:
public abstract class AbstractModelClass
{
public int AbstractModelClassId { get; set; } // this key is required but I want him to not to be because I don't want to have 2 PK's
public string Prop1 { get; set; }
}
public class ModelClass : AbstractModelClass // before this class was not inherited but now I need this
{
public int ModelClassId { get; set; }
public int Prop2 { get; set; }
}
Why can't the primary key be in the abstract class but in database it is different tables? Check out Table per Concrete Type (TPC) approach in EF. Good explanation here:
https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type-tpc-and-choosing-strategy-guidelines
Sample:
public abstract class BillingDetail
{
[DatabaseGenerated(DatabaseGenerationOption.None)]
public int BillingDetailId { get; set; }
public string Owner { get; set; }
public string Number { get; set; }
}
public class BankAccount : BillingDetail
{
public string BankName { get; set; }
public string Swift { get; set; }
}
public class CreditCard : BillingDetail
{
public int CardType { get; set; }
public string ExpiryMonth { get; set; }
public string ExpiryYear { get; set; }
}
public class InheritanceMappingContext : DbContext
{
public DbSet<BillingDetail> BillingDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BankAccount>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("BankAccounts");
});
modelBuilder.Entity<CreditCard>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("CreditCards");
});
}
}
In this case I don't see the purpose of AbstractModelClassId in AbstractModelClass so one solution would be not having it.
However is for some reason you need that property, but don't want that it gets into Db table then you could add [NotMapped] attribute to it.
[NotMapped]
public int AbstractModelClassId { get; set; }
I have a DB set models which inherit from base class model as Below.
Base Class:
public abstract class BaseModel
{
[Key]
public int Id { get; set; }
public bool IsActive { get; set; }
public int ModifiedBy { get; set; }
public DateTime ModifiedDate { get; set; }
public int AddedBy { get; set; }
public DateTime AddedDate { get; set; }
}
Child Class:
public class AccountType : BaseModel
{
//[Key]
//public int Account_Type_Id { get; set; }
[Required]
[MaxLength(50)]
public string Account_Type_Name { get; set; }
[Required]
[MaxLength(10)]
public string Account_Type_Code { get; set; }
[MaxLength(100)]
public string AccountType_Description { get; set; }
}
And my DBContext is as below:
public class BankApplicationContext :DbContext
{
public BankApplicationContext() : base("BankContextEntities")
{
Database.SetInitializer(new BankApplicationIntializer());
}
public virtual DbSet<AccountType> AccountTypes { get; set; }
public virtual DbSet<BaseModel> BaseModels { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseModel>().ToTable("BaseModel");
modelBuilder.Entity<AccountType>().ToTable("AccountType");
}
}
When the Database is created the table looks like this
But i need all the Audit columns from base class generated in child class as below:
AccountType:
Id (PK of Account Type)
AccountTypeName
AccountTypeCode
AccountTypeDescription
IsActive
ModifiedBy
ModifiedDate
AddedBy
AddedDate
You want a Table per Concrete Type structure.
But here is the problem: this only works for non-abstract classes.
If we assume that BaseModel is not abstract, you can change your table mapping to include the inherited properties:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<BaseModel>().Map(m => {
m.ToTable("BaseModel");
});
modelBuilder.Entity<AccountType>().Map(m => {
m.MapInheritedProperties();
m.ToTable("AccountType");
});
}
For supertypes like this, don't map them in the database at all. You don't really want the ability to query the database for all BaseModel types of any subtype, as they aren't not the same entity, and are not substitutable in any business logic.
Instead just delete the DbSet<BaseModel> property from your DbContext, and each BaseModel entity subtype will separately map and store the inherited properties.
I have 2 models which have exactly same fields, but I chose to make different models for them because I needed two different tables, one for each.
Earlier everything was working fine when I had two different tables for each model, but then I started using abstract base class because the code inside both the models were same.
Now I have a single table comprised of all the data that I save.
How can I create different tables for those two models.
public abstract class baseGrammar
{
[Key]
public int Id { get; set; }
[Required]
public string question { get; set; }
[Required]
public string ans { get; set; }
public string ruleId { get; set; }
public string ruleApplicable { get; set; }
[ForeignKey("ruleId")]
public virtual ruleTable RuleTable { get; set; }
}
The one shown above is my abstract base class.
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Just if someone intrested in ruleTable model.
public class ruleTable
{
[Key]
public string ruleId { get; set; }
public string topic { get; set; }
public string rule { get; set; }
public string example { get; set; }
public virtual ICollection<baseGrammar> BaseGrammar { get; set; }
}
Am also adding context class so as to provide better description
public class english : DbContext
{
public english() : base("name=localServerEng")
{
Database.SetInitializer<DbContext>(null);
Database.SetInitializer<english>(new UniDBInitializer<english>());
}
public virtual DbSet<adjective> adjectiveDb { get; set; }
public virtual DbSet<adverb> adverbDb { get; set; }
public virtual DbSet<alternativeVerb> alternativeVerbDb { get; set; }
public virtual DbSet<antonyms> antonymsDb { get; set; }
public virtual DbSet<article> articleDb { get; set; }
private class UniDBInitializer<T> : DropCreateDatabaseIfModelChanges<english>
{
}
public System.Data.Entity.DbSet<StructureSSC.Areas.AreaEnglish.Models.baseGrammar> baseGrammars { get; set; }
}
Image of SQL Server showing 1 table comprising of all columns instead of different tables
Add table data annotation:
[Table("TABLE_NAME")]
Your class will look like:
[Table("articles")]
public class article : baseGrammar
{
}
you can use Table-Per-Concrete Class (TPC) Pattern using This Configurations
on OnModelCreating fuction
modelBuilder.Entity<baseGrammar>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<article>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("article");
});
modelBuilder.Entity<adjective>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("adjective");
});
I have the following entities:
public abstract class Meter
{
public int MeterId { get; set; }
public string EANNumber { get; set; }
public string MeterNumber { get; set; }
[Required]
public virtual Premise Premise { get; set; }
public abstract void AddReading(CounterReading reading);
}
public class GasMeter : Meter
{
public virtual Counter Counter { get; private set; }
public override void AddReading(CounterReading reading)
{
Counter.Readings.Add(reading);
}
}
public class Premise
{
[Key]
public int PremiseId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
[Required]
public virtual Address Address { get; set; }
public string Type { get; set; }
public virtual GasMeter GasMeter { get; set; }
}
I have a 1:1 relation between a GasMeter and a Premise.
What must I do so that I can set myPremise.GasMeter = myMeter, and retrieve myPremise in later code with myMeter.Premise?
Edit
When setting it up via the Fluent API as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Premise>().HasOptional(p => p.GasMeter)
.WithRequired(m => m.Premise);
}
I get the following exception when running:
An exception of type 'System.Data.Entity.ModelConfiguration.ModelValidationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: One or more validation errors were detected during model generation:
Premise: FromRole: NavigationProperty 'Premise' is not valid. Type 'GasMeter' of FromRole 'Premise_GasMeter_Target' in AssociationType 'Premise_GasMeter' must exactly match with the type 'Meter' on which this NavigationProperty is declared on.
Does this mean that I can't use Navigation Properties with inheritance?
How would I solve my problem then?
I think you need to add the Id to each class for PK/FK relationship
public abstract class Meter
{
....
public int PremiseId
public virtual Premise Premise { get; set; }
}
and
public class Premise
{
....
public int GasMeterId
public virtual GasMeter GasMeter{ get; set; }
}
You probably don't need to link these two entities as virtual properties within each other. Try modify Meter class to keep the PremiseId since it is the primary key of Premise table then get the Premise entity using Select(x => x.PremiseId == aMeter.PremiseId).SingleOrDefault() to get the Premise mapped to this GasMeter
public abstract class Meter
{
public int MeterId { get; set; }
public string EANNumber { get; set; }
public string MeterNumber { get; set; }
public int PremiseId { get; set; }
public abstract void AddReading(CounterReading reading);
}
SET
var aPremise = new Premise();
var aMeter = new GasMeter();
aPremise.GasMeter = aMeter;
aMeter.PremiseId = aPremise.PremiseId;
GET
var thePremise = _repository.Set<Premise>.Select(x => x.PremiseId == aMeter.PremiseId).SingleOrDefault();