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; }
Related
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);
}
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 3 models with fields in it like the following:
public class RootObject
{
[Key]
public int RootObjectId { get; set; }
[ForeignKey("RootObjectId")]
public virtual AObject AObject { get; set; }
[ForeignKey("RootObjectId")]
public virtual BObject BObject { get; set; }
public string Name { get; set; }
}
public class AObject
{
[Key]
public int AObjectId { get; set; }
//Other fields
}
public class BObject
{
[Key]
public int BObjectId { get; set; }
//Other fields
}
I want it so that if I were to visually inspect the RootObject table I would see see a list of RootObjectId's and Name's. For ease, lets assume even numbered RootObjectId's are mapped to AObjectId's and odds are mapped to BObjectId's. If I were to visually inspect AObject, I would expect to see the ID's 2, 4, 6, ... that are FK's for RootObject. If were to visually inspect BObject, I would expect to see the ID's 1, 3, 5, ... that are FK's for RootObject.
Currently, when I try this approach I get the following error:
"An error occurred while updating the entries...Referential integrity constraint violations. A Dependent Role has multiple principals with different values."
I tried to remove the FK attributes in RootObject but that created 2 additional columns in RootObject that were populated with ID numbers. I don't want this since every RootObject has either one AObject or one BObject. It can't have both.
To me, you are looking for something for which the TPT (Table per Type) approach in Entity Framework could be a solution. Applied to your case (there are many approaches, but this I tested and it works):
public class RootObject
{
[Key]
public int RootObjectId { get; set; }
public string Name { get; set; }
}
[Table("AObjects")]
public class AObject : RootObject
{
//Other fields
public string AField { get; set; }
}
[Table("BObjects")]
public class BObject : RootObject
{
//Other fields
public string BField { get; set; }
}
For the DbContext class:
public DbSet<RootObject> RootObjects { get; set; }
public DbSet<AObject> AObjects { get; set; }
public DbSet<BObject> BObjects { get; set; }
Seed example:
AObject a1 = new AObject() { Name = "ImA", AField = "adata" };
BObject b1 = new BObject() { Name = "ImB", BField = "bdata" };
context.AObjects.Add(a1);
context.BObjects.Add(b1);
context.SaveChanges();
I don't think it is possible to use ONE column to be a foreign key to two different tables. You should rather think of two (optional) FK like:
public class RootObject
{
[Key]
public int RootObjectId { get; set; }
public int? EvensAObjectId { get; set; }
public int? OddsBObjectId { get; set; }
[ForeignKey("EvensAObjectId")]
public virtual AObject AObject { get; set; }
[ForeignKey("OddsBObjectId")]
public virtual BObject BObject { get; set; }
public string Name { get; set; }
}
To set up 1-0..1 relationships you need to define the relationships explicitly during model configuration.
public class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AObject>()
.HasRequired(e => e.RootObject).WithOptional(r => r.AObject);
modelBuilder.Entity<BObject>()
.HasRequired(e => e.RootObject).WithOptional(r => r.BObject);
base.OnModelCreating(modelBuilder);
}
public virtual DbSet<RootObject> RootObjects { get; set; }
public virtual DbSet<AObject> AObjects { get; set; }
public virtual DbSet<BObject> BObjects { get; set; }
}
public class RootObject
{
[Key]
public int RootObjectId { get; set; }
public virtual AObject AObject { get; set; }
public virtual BObject BObject { get; set; }
public string Name { get; set; }
}
public class AObject
{
[Key]
public int AObjectId { get; set; }
public virtual RootObject RootObject { get; set; }
}
public class BObject
{
[Key]
public int BObjectId { get; set; }
public virtual RootObject RootObject { get; set; }
}
You'll want to be really careful when setting RootObject.AObject and RootObject.BObject, as if there is already a related row you will get an error when you save. Also, I don't think there's any way to get EF enforce the constraint that each RootObject must have either an AObject or a BObject, but not both - you'd need to enforce that in your code.
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();
I'm using Entity Framework 6 and I have the following base class
public class ModelBase
{
public int ModelBaseId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IList<Notification> Notifications { get; set; }
public IList<Assignable> Viewers { get; set; }
}
and a derived
public class Model : ModelBase
{
public int ModelId { get; set; }
public User Owner { get; set; }
public DateTime Date { get; set; }
public string Observations { get; set; }
public User Assignament { get; set; }
}
The ModelBase properties are populated with the same data for all derived class instances. I want to map the base entity properties with a table(A) and properties of the derived class with another table(B). And when I add Model just insert in the table B.
Is there any way to do this?? Is something complex to explain but I've tried to simplify my problem.
Thanks in advance!
Assuming you are using Codefirst...
You should try mapping them..
public class ModelBaseMap:EntityTypeConfiguration<ModelBase>
{
public ModelBaseMap()
{
this.toTable("ModelBase");
}
}
public class ModelMap:EntityTypeConfiguration<Model>
{
public ModelBaseMap()
{
this.toTable("Model");
}
}
I think it should work