I am rather new to EntityFramework's Code First approach and I'm getting the following error when I try to to create a reusable complex type that references an entity type.
Models:
class Bank
{
public int Code { get; set; }
public string Name { get; set; }
}
class BankAccount
{
public Bank Bank { get; set; }
public int BankId { get; set; }
public int Agency { get; set; }
public int Account { get; set; }
}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public BankAccount BankAccount { get; set; }
}
DbContext:
class DemoContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bank>().HasKey(b => b.Code);
modelBuilder.ComplexType<BankAccount>();
}
}
When I try to add a Migration I get the following error:
One or more validation errors were detected during model generation:
ComplexTypeProblem.EF.Bank: Name: Each type name in a schema must be unique. Type name 'Bank' is already defined.
ComplexTypeProblem.EF.Bank: : EntityType 'Bank' has no key defined. Define the key for this EntityType.
Banks: EntityType: EntitySet 'Banks' is based on type 'Bank' that has no keys defined.
Is there any caveat to implement this kind of relationship where a ComplexType has a navigation property to an EntityType?
Thanks
You can't have a ComplexType containing an EntityType. Only the other way around.
ComplexTypes are simply properties for an entity, and they should work on your code just like normal fields.
Are you trying to set up a 1 -> many mapping for the Bank / BankAccounts? If so, I would recommend the following:
public partial class Bank
{
public Bank()
{
BankAccounts = new List<BankAccount>();
}
public int Code { get; set;}
public string Name { get; set;}
public virtual ICollection<BankAccount> BankAccounts { get; set;}
}
public partial class BankAccount
{
public int BankId { get; set;}
public int Agency { get; set;}
public int Account { get; set;}
public virtual Bank Bank { get; set;}
}
And change the OnModelCreating method as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bank>().HasKey(b => b.Code);
modelBuilder.Entity<Bank>().HasMany(m => m.BankAccounts).WithRequired(r => r.Bank).HasForeignKey(f => f.BankId).WillCascadeOnDelete(false);
}
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 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 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();