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...
Related
I'm working on a trucking API using Entity Framework (EF) Core. Basic CRUD operations are working fine using the repository pattern. There is an error in
configurations I am implementing, however.
I want to obtain multiple trailers and trucks associated with single load, reflecting the one-to-many relationship.
public class LoadConfiguration : IEntityTypeConfiguration<Load>
{
public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<Load> builder)
{
builder.Property(p=>p.Id).IsRequired();
builder.HasOne(t=>t.Customer).WithMany().HasForeignKey(p=>p.CustomerId);
builder.Property(p=>p.LoadedFrom).IsRequired();
builder.HasMany(p=>p.Trailer).WithOne().HasForeignKey(t=>t.TrailerId);
builder.HasMany(p=>p.Truck).WithOne().HasForeignKey(t=>t.TruckId);
builder.Property(p=>p.Destination).IsRequired();
}
}
public class Truck:BaseEntity
{
public int PlateNo { get; set; }
public string ModelName { get; set; }
public Location StateCode { get; set; }
public int PollutionCertificateValidity { get; set; }
public int DateOfPurchase { get; set; }
public int FitnessCertificateValidity { get; set; }
}
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public Trailer Trailer { get; set; }
public int TrailerId { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
public string Destination { get; set; }
}
public class Trailer:BaseEntity
{
public int TrailerCapacity { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
}
public class BaseEntity
{
public int Id { get; set; }
}
A one-to-many relationship is defined by using navigation collections, that has the capacity to hold many Trucks and Trailers. You can choose the collection type freely, but I would suggest ICollection generic type.
Modify your Load class as follows:
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public string Destination { get; set; }
// navigation collections
public ICollection<Trailer> Trailers { get; set; }
public ICollection<Truck> Trucks { get; set; }
}
You will then be able to set up the relationship in your LoadConfiguration class by using
the pluralized name:
builder.HasMany(p=>p.Trailers).WithOne();
builder.HasMany(p=>p.Trucks).WithOne();
.. even though EF Core will be smart enough to figure out the relation by convention so the fluent configuration is redundant.
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've been trying to get the following to work for a few hours now. But I can't seem to find out what I'm doing wrong here.
I'm using Fluent Nhibernate automapper (and some overrides) to get this structure to work.
public class Game:IKeyed<Guid>
{
public virtual Guid Id { get; set; }
//properties
}
public class Team : IKeyed<Guid>
{
public virtual Guid Id { get; set; }
//Other properties
public virtual IList<GameTeam> GameTeams { get; set; }
}
public class GameTeam:IKeyed<GameTeamId>
{
public virtual GameTeamId Id { get; set; }
public virtual int CurrentRound { get; set; }
public virtual IList<GameTeamRound> Rounds { get; set; }
}
public class GameTeamId
{
public virtual Game Game { get; set; }
public virtual Team Team { get; set; }
//equals stuff
}
public class GameTeamRound : IKeyed<GameTeamRoundId>
{
public virtual GameTeamRoundId Id { get; set; }
//Properties
public virtual IList<TeamRoundDecision> Decisions { get; set; }
}
public class GameTeamRoundId
{
public virtual GameTeam GameTeam { get; set; }
public virtual int RoundNumber { get; set; }
}
The GameTeam relation is something I can manage. But the GameTeamRound link is going a bit to far for the moment. :)
I even have a level deeper. But I don't want to make the question more complicated.
I'm using NHibernate to generate my database for me. So I'm starting from my model.
To make this work I'm using some mapping overrides to make sure that these composite keys are working.
public class GameTeamOverride : IAutoMappingOverride<GameTeam>
{
public void Override(AutoMapping<GameTeam> mapping)
{
//mapping.IgnoreProperty(gt => gt.Id);
mapping.CompositeId(gt => gt.Id)
.KeyProperty(id => id.Game.Id, "GameId")
.KeyProperty(id => id.Team.Id, "TeamId");
}
}
public class GameTeamRoundOverride : IAutoMappingOverride<GameTeamRound>
{
public void Override(AutoMapping<GameTeamRound> mapping)
{
//mapping.IgnoreProperty(gtr => gtr.Id);
mapping.CompositeId(gtr => gtr.Id)
.KeyProperty(id => id.GameTeam.Id.Game.Id, "GameId")
.KeyProperty(id => id.GameTeam.Id.Team.Id, "TeamId")
.KeyProperty(id => id.RoundNumber, "RoundId");
}
}
I've tried al sort of things. If you could point me into the right direction, that would be great. :)
Thanks
Tim
Why the extra class for the composite key?
It should be working somehow like this:
Entities:
public class Game:IKeyed<Guid>
{
public virtual Guid Id { get; set; }
//properties
}
public class Team : IKeyed<Guid>
{
public virtual Guid Id { get; set; }
//Other properties
public virtual IList<GameTeam> GameTeams { get; set; }
}
public class GameTeam:IKeyed<GameTeamId>
{
public virtual Game Game { get; set; }
public virtual Team Team { get; set; }
public virtual int CurrentRound { get; set; }
public virtual IList<GameTeamRound> Rounds { get; set; }
}
public class GameTeamRound : IKeyed<GameTeamRoundId>
{
public virtual GameTeam GameTeam { get; set; }
public virtual int RoundNumber { get; set; }
//Properties
public virtual IList<TeamRoundDecision> Decisions { get; set; }
}
Mappings:
public class GameTeamOverride : IAutoMappingOverride<GameTeam>
{
public void Override(AutoMapping<GameTeam> mapping)
{
mapping.CompositeId()
.KeyReference(id => id.Game, "GameId")
.KeyReference(id => id.Team, "TeamId");
}
}
public class GameTeamRoundOverride : IAutoMappingOverride<GameTeamRound>
{
public void Override(AutoMapping<GameTeamRound> mapping)
{
mapping.CompositeId()
.KeyReference(id => id.GameTeam.Game, "GameId")
.KeyReference(id => id.GameTeam.Team, "TeamId")
.KeyProperty(id => id.RoundNumber, "RoundId");
}
}
Please note: I am not sure if it even is possible to use this kind of nesting in a composite ID. If not, you would need to add properties for Game and Team and delegate the calls to them to GameTeam.Game and GameTeam.Team respectivly.
Additionally, please re-consider your design. Composite keys are discouraged for newly written applications. If they can be avoided, avoid them.
I get the following error when using the below mappings:
NHibernate.MappingException: Association references unmapped class: GeoTriggers.Domain.Entities.ITrigger
Entities:
public class Profile
{
public virtual int Id { get; set; }
public virtual string Password { get; set; }
public virtual string Username { get; set; }
public virtual IList<ITrigger> Triggers { get; set; }
}
public interface ITrigger
{
int Id { get; set; }
decimal Latitude { get; set; }
decimal Longitude { get; set; }
decimal Radius { get; set; }
}
public class EmailTrigger : ITrigger
{
public virtual string RecipientAddress { get; set; }
public virtual string SenderAddress { get; set; }
public virtual string Subject { get; set; }
public virtual string Body { get; set; }
#region ITrigger Members
public virtual int Id { get; set; }
public virtual decimal Latitude { get; set; }
public virtual decimal Longitude { get; set; }
public virtual decimal Radius { get; set; }
#endregion
}
Using the following class maps:
public sealed class ProfileMap : ClassMap<Profile>
{
public ProfileMap()
{
HasMany(x => x.Triggers).Cascade.All();
}
}
public sealed class TriggerClassMap : ClassMap<ITrigger>
{
public TriggerClassMap()
{
}
}
public sealed class EmailTriggerClassMap : SubclassMap<EmailTrigger>
{
public EmailTriggerClassMap()
{
}
}
Using the following FluentNHibernate configuration:
FluentConfiguration fluent = Fluently.Configure(_configuration)
.Mappings(
x => x.AutoMappings.Add(
AutoMap.Assemblies(typeof (TEntity).Assembly).Where(
assembly => assembly.Namespace.Contains("Entities"))));
All my entities are in a namespace that includes the word "Entities". I did this to avoid having to make all my entities inherit from a common base.
I am trying to keep my configuration to a minimum, so I have opted to allow Fluent NHibernate to run free with its default conventions. Normally, I don't even have class map files. But, now, since I'm trying to map a list of objects that implement a common interface, it has gotten sticky.
The error says that an association (I assume the one-to-many in the ProfileMap) references an unmapped class (ITrigger). According to the docs, it sounds like all I need to have is a class map for the base/interface and subclass maps for the implementations. I have that. What am I missing?