Fluent NHibernate complex Composite Id's - c#

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.

Related

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

Trying to create different tables for different models which are using same base abstract class

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

2 Foreign Keys, 1 Navigation Property to pull through both

Given the following rough code-first schema, the goal would appear to be quite simple. An Invoice can either be from or to a Company, and the Invoices collection should contain all invoices regardless of which it is.
public class Company
{
public int Id { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public int FromCompanyId { get; set; }
public int ToCompanyId { get; set; }
public virtual Company FromCompany { get; set; }
public virtual Company ToCompany { get; set; }
}
You'll note in a Migration that a third Company_Id is generated for obvious reasons to support the Invoices navigation property as EF only appears to support a 1 Nav Prop -> 1 FK arrangement.
My question is whether or not it is possible to have the Invoices property contain both, or if I should map them individually (ie. IC<Inv> InvoicesFrom, IC<Inv> InvoicesTo) and create a client-side collection to have both manually.
I have tried:
Using InverseProperty on both FromCompany and ToCompany, which confuses EF as it can't determine the principal end of the relationship.
[ForeignKey(nameof(FromCompanyId)), InverseProperty(nameof(Company.Invoices))]
public virtual Company FromCompany { get; set; }
[ForeignKey(nameof(ToCompanyId)), InverseProperty(nameof(Company.Invoices))]
public virtual Company ToCompany { get; set; }
Using fluent API to map them, but it only takes into account the second which makes sense from a code perspective.
modelBuilder.Entity<Company>()
.HasMany(m => m.Invoices)
.WithRequired(m => m.ToCompany)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Company>()
.HasMany(m => m.Invoices)
.WithRequired(m => m.FromCompany)
.WillCascadeOnDelete(false);
There's of course no major issue if this isn't possible, I just could have sworn I've done it before.
For posterity, here is a complete version of the workaround to maintain an IEnumerable<Invoices> from company that contains both of the sets put together.
public class MyContext : DbContext
{
public MyContext() : base("DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Company>().HasMany(c => c.InvoicesFrom).WithRequired(i => i.FromCompany).WillCascadeOnDelete(false);
modelBuilder.Entity<Company>().HasMany(c => c.InvoicesTo).WithRequired(i => i.ToCompany).WillCascadeOnDelete(false);
}
public DbSet<Company> Companies { get; set; }
public DbSet<Invoice> Invoices { get; set; }
}
public class Company
{
public int Id { get; set; }
public virtual ICollection<Invoice> InvoicesFrom { get; set; }
public virtual ICollection<Invoice> InvoicesTo { get; set; }
[NotMapped]
public IEnumerable<Invoice> Invoices
{
get {
return InvoicesFrom.Union(InvoicesTo);
}
}
}
public class Invoice
{
public int Id { get; set; }
public int FromCompanyId { get; set; }
public int ToCompanyId { get; set; }
public virtual Company FromCompany { get; set; }
public virtual Company ToCompany { get; set; }
}

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...

Mapping a list of implementations of an interface with Fluent NHibernate

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?

Categories