How do I get Navigation Properties working? - c#

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();

Related

One to zero or one relationship with Code first c#

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);
}

Entity Framework Code First - Abstract model class without primary key

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; }

Settings' threw an exception of type 'System.Data.SqlClient.SqlException'

I got the exception and can't figure it out.
Settings = '((BandwidthRestriction.Models.SettingRespository)settingRespository).Settings' threw an exception of type 'System.Data.SqlClient.SqlException'
I have two tables.
namespace BandwidthRestriction.Models
{
[Table("Settings")]
public class Setting
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string DefaultValue { get; set; }
public string Classification { get; set; }
public virtual FacilitySettingOverride FacilitySettingOverride { get; set; }
}
}
And
namespace BandwidthRestriction.Models
{
[Table("FacilitySettingOverride")]
public class FacilitySettingOverride
{
[Key]
public int FacilityId { get; set; }
public int SettingId { get; set; }
public string Value { get; set; }
public virtual ICollection<Setting> Settings { get; set; }
public virtual ICollection<Facility> Facilities { get; set; }
}
}
Another table
namespace BandwidthRestriction.Models
{
[Table("Facilities")]
public class Facility
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<FacilitySettingOverride> FacilitySettingOverrides { get; set; }
}
}
The table's structure likes
[![12][1]][1]
Also I have the correspond dbcontext as
public class SettingDbContext : DbContext
{
public DbSet<Setting> Settings { get; set; }
public DbSet<FacilitySettingOverride> FacilitySettingOverride { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=11.53.63.94;Initial Catalog=AAA;User ID=sa;password=password;Application Name=XXX");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
In the respository, I have
namespace BandwidthRestriction.Models
{
public class SettingRespository : ISettingRespository
{
public List<Setting> GetAllSettings()
{
return Settings.ToList();
}
public IEnumerable<Setting> Settings
{
get
{
List<Setting> settingList;
using (SettingDbContext context = new SettingDbContext())
{
settingList = context.Settings.ToList();
}
return settingList;
}
}
In the controller, I passed the DI.
[Route("api/[controller]")]
public class BandwidthController : Controller
{
private readonly ISettingRespository _settingRespository;
public BandwidthController(ISettingRespository settingRespository)
{
_settingRespository = settingRespository;
}
However when I hover the _settingRespository. I see the exception:
Settings = '((BandwidthRestriction.Models.SettingRespository)settingRespository).Settings' threw an exception of type 'System.InvalidOperationException'
EDIT:
Per the comment, I fixed the table name misspelling issue. The error is SqlException: Invalid column name 'FacilitySettingOverrideSettingId', But I found a similar question at stackoverflow. Maybe I used code first wrongly?
In another word, the table FacilitySettingOverride, it doesn't have the primary key. Is it the cause?
EDIT-1
Per comments. I redesigned the DB. I think that Setting-FacilitySettingOverride to be 1:1
[![new][3]][3]
And
[Table("FacilitySettingOverride")]
public class FacilitySettingOverride
{
[Key]
public int FacilityId { get; set; }
public string Value { get; set; }
public int SettingId { get; set; }
public virtual Facility Facility { get; set; }
public virtual Setting Setting { get; set; }
}
The new error is
SqlException: Invalid column name 'FacilityId1'.
in the code:
public int GetFacilityBandwidthSetting(int facilityId)
{
using (SettingDbContext context = new SettingDbContext())
{
var setting = context.Settings.Single(s => s.Name == SettingType.TotalBandwidth.ToString());
var value = context.FacilitySettingOverride.SingleOrDefault(x => x.FacilityId == facilityId
&& x.SettingId == setting.Id);
if (value == null)
return int.Parse(setting.DefaultValue);
return int.Parse(value.Value);
}
}
and
[Table("Facilities")]
public class Facility
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
I can getting Setting but FacilitySettingOverride in the context.
Create POCO class in EF 7 code first
Basically they are same question. The key issue is to create the correct POCO class.

Issue mapping code first inherited navigation property

When I try to generate a database using codefirst with inehrited navigation properties I get an error:
error 0040: Type Point_Countries is not defined in namespace xx.xxx (Alias=Self).
I have 3 classes
public class PointBase
{
public int PointID { get; set; }
public virtual Point Point { get; set; }
}
public class Point
{
public int PointID { get; set; }
public DbGeography Data { get; set; }
public virtual ICollection<Country> Countries { get; set; }
}
public Country : PointBase
{
public int CountryID { get; set; }
public string Name { get; set; }
}
Afterwards, I created an EntityTypeConfiguration for point
internal class PointMap : EntityTypeConfiguration<Point>
{
public PointMap()
{
HasMany(x => x.Countries).WithRequired(x => x.Point).HasForeignKey(x => x.PointID);
}
}
I can't seem to figure out why this error shows up...

Entity Framework - Reuse Complex Type

I have an Entity in Code First Entity framework that currently looks like this:
public class Entity
{
// snip ...
public string OriginalDepartment { get; set; }
public string OriginalQueue { get; set; }
public string CurrentDepartment { get; set; }
public string CurrentQueue { get; set; }
}
I would like to create Complex Type for these types as something like this:
public class Location
{
public string Department { get; set; }
public string Queue { get; set; }
}
I'd like to use this same type for both Current and Original:
public Location Original { get; set; }
public Location Current { get; set; }
Is this possible, or do I need to create two complex types CurrentLocation and OriginalLocation?
public class OriginalLocation
{
public string Department { get; set; }
public string Queue { get; set; }
}
public class CurrentLocation
{
public string Department { get; set; }
public string Queue { get; set; }
}
It is supported out of box, you do not need to create two complex types.
You can also configure your complex types explicitely with model builder
modelBuilder.ComplexType<Location>();
To customize column names, you should configure them from parent entity configuration
public class Location
{
public string Department { get; set; }
public string Queue { get; set; }
}
public class MyEntity
{
public int Id { get; set; }
public Location Original { get; set; }
public Location Current { get; set; }
}
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Location>();
modelBuilder.Entity<MyEntity>().Property(x => x.Current.Queue).HasColumnName("myCustomColumnName");
}
}
This will map MyEntity.Current.Queue to myCustomName column

Categories