Foreign Key to a table with 2 columns primary key (CompositeId) - c#

I've defined the primary key as following:
CompositeId()
.KeyProperty(x => x.Id)
.KeyProperty(x => x.Type);
I've tried the following:
References(x => x.EntityWith2ColsPK);
And failed with:
Foreign key (Fk_MyEntity_EntityWith2ColsPK:MyEntities [Fk_EntityWith2ColsPK])) must have same number of columns as the referenced primary key (EntityWith2ColsPKs [Id, Type])
How can I reference EntityWith2ColsPK from another entity?
Update:
I've tried the following (according to AlfeG's comment):
HasMany<EntityWith2ColsPK>(x => x.EntityWith2ColsPK).KeyColumns.Add("Id", "Type").Cascade.All();
Which failed with:
Custom type does not implement UserCollectionType: EntityWith2ColsPK
But anyway I don't want a 1 to many relation, I want a 1 to 1 relation. Still, I can't make either of them work.
Also, I've tried:
HasOne<EntityWith2ColsPK>(x => x.EntityWith2ColsPK).PropertyRef(x => x.Id).PropertyRef(x => x.Type);
Which fails with:
NHibernate.MappingException : property not found: Type on entity EntityWith2ColsPK
What can I do for this to really work?
I managed to achieve something in the db.. but yet, for some reason I suspect it maps the property "Type" twice, because I want it to be both part of the Primary Key, and part of the Foreign Key..
This is what I did:
References(x => x.EntityWith2ColsPK).Columns("EntityWith2ColsPKId", "Type").Formula("Id = :EntityWith2ColsPKId AND Type = :Type");
But I received the following exception:
System.IndexOutOfRangeException : Invalid index 8 for this SqlParameterCollection with Count=8.
Because the mapping of this entity is same as EntityWith2ColsPK:
CompositeId()
.KeyProperty(x => x.Id)
.KeyProperty(x => ((ILocalizedEntity) x).Language);
HELP!

You can use something like this since you aren't using cascade anyway on your Reference
References(x => x.EntityWith2ColsPK)
.Columns(new string[] { "ID", "TYPE" })
.Not.Update()
.Not.Insert();

Related

System.NullReferenceException throw when build HasForeignKey [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have code c# like this
builder.Entity<EnPpTime>()
.HasOne(a => a.EnPpTimeInMeta).WithOne(b => b.EnPpTime)
.HasForeignKey<EnPpTimeInMeta>(e => e.Id);
when run the programe throw exception
System.NullReferenceException: Object reference not set to an instance of an object.
[2021-11-11T07:06:30.667Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.ForeignKeyAttributeConvention.UpdateRelationshipBuilder(IConventionForeignKeyBuilder relationshipBuilder, IConventionContext context)
[2021-11-11T07:06:30.668Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.ForeignKeyAttributeConvention.ProcessForeignKeyAdded(IConventionForeignKeyBuilder relationshipBuilder, IConventionContext`1 context)
[2021-11-11T07:06:30.668Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnForeignKeyAdded(IConventionForeignKeyBuilder relationshipBuilder)
[2021-11-11T07:06:30.669Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnForeignKeyAddedNode.Run(ConventionDispatcher dispatcher)
[2021-11-11T07:06:30.669Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.DelayedConventionScope.Run(ConventionDispatcher dispatcher)
[2021-11-11T07:06:30.670Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Run()
[2021-11-11T07:06:30.670Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Run(IConventionForeignKey foreignKey)
[2021-11-11T07:06:30.671Z] at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionBatchExtensions.Run(IConventionBatch batch, InternalForeignKeyBuilder relationshipBuilder)
[2021-11-11T07:06:30.671Z] at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceReferenceBuilder.HasForeignKeyBuilder(EntityType dependentEntityType, String dependentEntityTypeName, Func`3 hasForeignKey)
[2021-11-11T07:06:30.672Z] at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceReferenceBuilder.HasForeignKeyBuilder(EntityType dependentEntityType, String dependentEntityTypeName, IReadOnlyList`1 foreignKeyMembers)
[2021-11-11T07:06:30.672Z] at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceReferenceBuilder`2.HasForeignKey[TDependentEntity](Expression`1 foreignKeyExpression)
How to fix this exception
Looks like You're missing the
.HasPrincipalKey
in this case it will default to look for entityname suffixed "ID" in Your case "EnPpTimeID" which if it doesn't exist will fail
It is a very good idea when using entity framework always to use EntityTypeNameID for identity column in this case you will not have to explicitly defined the foreign and principal key
This appears to be a known bug when there are multiple one-to-one relationships between two entity types.
Calling entity.HasOne(d => d.Parent).WithOne(p => p.Child) before entity.HasKey() solved the problem for me. Read on for details:
I had the same problem using EF core 5 - .HasForeignKey() throws the said exception.
The DbContext was scaffolded. Scaffoling does not generate .HasPrincipalKey() for single-column foreign keys. Manually adding .HasPrincipalKey() did not fix the problem. My tables however had two 1:1 relationships. This means that the child table has 4 constraints: two foreign keys and two unique keys on the same columns as the FKs. The scaffolded code is:
builder.Entity<Child>(entity =>
{
// 2 unique keys.
entity.HasKey(e => e.ParentId);
entity.HasIndex(e => new { e.OtherId, e.ParentId }).IsUnique();
entity.Property(e => e.ParentId).ValueGeneratedNever();
// 2 foreign keys.
entity.HasOne(d => d.Parent).WithOne(p => p.Child)
.HasForeignKey<Child>(d => d.ParentId); // <---- Throws exception!
entity.HasOne(d => d.Parent2).WithOne(p => p.Child2)
.HasPrincipalKey<Parent>(p => new { p.OtherId, p.Id })
.HasForeignKey<Child>(d => new { d.OtherId, d.ParentId });
});
When I moved entity.HasOne(d => d.Parent).WithOne(p => p.Child)... before entity.HasKey() the problem disappeared. However, you should not modify generated code. I inherited the scaffolded DbContext and in the subclass wrote this:
protected override void OnModelCreating(ModelBuilder builder)
{
// Bugfix:
builder.Entity<Child>().HasOne(d => d.Parent).WithOne(p => p.Child);
// You don't even need .HasForeignKey() here. It will be called inside base.OnModelCreating(). Duplicating .HasOne() and .WithOne() works fine.
base.OnModelCreating(builder);
}

Strange Foreign Key constraint in fluent mapped SQLite

I have a model written using FluentNhibernate and I am trying to create some test data in an in-memory SQLite database.
var fConfig =
Fluently.Configure()
.Database(config)
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(exampleClass))
.Conventions.Add(AutoImport.Never())
.Conventions.Add(new SQLiteGeometryTypeConvention())
.Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Never()))
.ExposeConfiguration(cfg => configuration = cfg);
The problem I am getting is that one of the tables is created like this in the generated sql something like this :
create table myTable (
myID integer primary key autoincrement,
...{skip normal columns}
ForeignTable1ID INT,
ParentID BIGINT,
constraint FK1A2E045AFEC6908F foreign key (ForeignTable1ID) references ForeignTable1,
constraint FK1A2E045ABB4EBD1F foreign key (ParentID) references Parent,
constraint FKE21911CF6853D06E foreign key (myID) references Parent
)
I don't want that third constraint but don't know what's causing it!
The effect being that I can only create records in myTable which have valid foreign keys BUT ALSO have a myID value which exists in the Parent table. This is unnecessary, and I can't see what's causing it.
The mapping file looks like this:
Table("dbo.PalslagInventering");
Id(x => x.myId).Column("myID");
References(x => x.ForeignTable1).Column("ForeignTable1ID");
References(x => x.Parent).Column("ParentID");
Map(x => x.{other columns here});
HasMany(x => x.Child).KeyColumn("myID");
HasOne(x => x.Child2).ForeignKey("MyID");
HasMany(x => x.Child2).KeyColumn("MyID").ForeignKeyConstraintName("Child2");
HasMany(x => x.Child3).KeyColumn("MyID").ForeignKeyConstraintName("Child3");
The parent table (being referenced) has a simple
public virtual IList<MyTable> myTableRecords { get; private set; }
Type of code mapped as:
HasMany(x => x.myTableRecords)
.KeyColumn("myID")
.Inverse();
What is causing the "foreign key" reference back to it's own myId?
The error seems to be:
HasMany(x => x.myTableRecords)
.KeyColumn("myID")
.Inverse();
Where it's the Child table (i.e. "MyTable") which has the ID "myID" and the KeyColumn should point to the Parent table (i.e. the foreign key in "MyTable").
So:
HasMany(x => x.myTableRecords)
.KeyColumn("ParentID")
.Inverse();
Seems to have fixed the problem. That the error was in the parent class's mapping file meant I missed it at first.

Fluent NHibernate PersistentSpecification Test fails

I currently have these Maps
public class CountryMap : ClassMap<Country>
{
public CountryMap()
{
Table("tblCountry");
Id(x => x.Id, "intCountryId");
Map(x => x.Name, "strCountryName");
HasMany(x => x.FlagImages)
.Table("tblImage")
.KeyColumn("intRelId")
.Where("intObjId=29")
.Not.Cascade.All();
}
}
public class ImageMap : ClassMap<Image>
{
public ImageMap()
{
Table("tblImage");
Id(x => x.Id, "intImgId");
Map(x => x.ObjId, "intObjId");
Map(x => x.RelId, "intRelId");
Map(x => x.ImageName, "strImage");
}
}
and the following persistent specification test:
PersistenceSpecification<Image>(session)()
.CheckProperty(r => r.Id, 1)
.CheckProperty(r => r.ImageName, "ss")
.CheckProperty(r => r.ObjId, (int)ObjectType.Country)
.CheckProperty(r => r.RelId, 102)
.VerifyTheMappings();
Somewhat the test of Image failed.
The failure message is
Assert.AreEqual failed. Expected:<(null)>. Actual:<NHibernate.Exceptions.GenericADOException: could not insert: [HansaCrew.Models.Images.Image][SQL: INSERT INTO tblImage (intObjId, intRelId, strImage) VALUES (?, ?, ?); select last_insert_rowid()] ---> System.Data.SQLite.SQLiteException: constraint failed
foreign key constraint failed
I've checked it for 2 days and haven't find the reason. Any help?
Okay, I got it.
The problem of this piece of code lies here
HasMany(x => x.FlagImages)
.Table("tblImage")
.KeyColumn("intRelId")
.Where("intObjId=29")
.Not.Cascade.All();
NHibernate automatically creates a foreign key constraint with tblImage, which has a lot of intRelIds which is not in the column of intCountryId in the table tblCountry. That's why the test fails saying foreign key constraint failure.
The way to solve this problem is to create a view in the database and then map the view into the Image model

Possible to use CustomSqlType with CompositeId?

Working with legacy tables, need to create a CompositeId based on two char(3) fields. Don't see any overloads that make this possible with Fluent.
The mapping I'm attempting looks like this:
CompositeId()
.KeyProperty(x => x.LegacyEntity1Id, "LegacyEntity1Id")
.KeyProperty(x => x.LegacyEntity2Id, "LegacyEntity2Id");
Map(x => x.LegacyEntity1Id).CustomSqlType("char(3)");
Map(x => x.LegacyEntity2Id).CustomSqlType("char(3)");
I've also tried:
CompositeId()
.KeyReference(x => x.LegacyEntity1, "LegacyEntity1Id")
.KeyReference(x => x.LegacyEntity2, "LegacyEntity2Id");
Map(x => x.LegacyEntity1Id).CustomSqlType("char(3)");
Map(x => x.LegacyEntity2Id).CustomSqlType("char(3)");
Both result in the same outcome - the table gets generated with a proper composite id, but both columns are the default nvarchar(255). As a result, the foreign keys fail to generate and I get an exception, since the parent tables are char(3).
Is this not possible to map via Fluent?
If not, is there any real difference in mapping it like this*:
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.LegacityEntity1Id).CustomSqlType("char(3)");
Map(x => x.LegacityEntity2Id).CustomSqlType("char(3)");
References(x => x.LegacityEntity1).Column("LegacityEntity1Id").UniqueKey("1").Not.Nullable();
References(x => x.LegacityEntity2).Column("LegacityEntity2Id").UniqueKey("1").Not.Nullable();
* I do have the ability to modify the tables slightly (enough to add an identity), since the legacy tables are being ETLed into a local SQL instance.
Or is there another alternative approach? Can't use a HasManyToMany in this case, for what it's worth (will have a payload).
KeyReference will search the map of the referenced entity and uses the sqltype there. go to the referenced entities and specify Id(x => x.Id).Length(3).
This is how to do it in 2017:
CompositeId()
.KeyProperty(
x => x.LegacyEntity1Id,
k => k.ColumnName("LegacyEntity1Id").Type("AnsiString").Length(3))
.KeyProperty(
x => x.LegacyEntity2Id,
k => k.ColumnName("LegacyEntity2Id").Type("AnsiString").Length(3))

NHibernate fluent update problem (one to many relation)

I have a object OtherFaclilityEntity which contains a IList of OtherFcs objects, and the mapping is as follows:
public OtherFacilityMap()
{
Schema("SOME");
Table("OTHER_FACILITY");
Id(x => x.Id, "OTHER_FACILITY_S").GeneratedBy.TriggerIdentity();
Map(x => x.RowCreator, "ROW_CREATOR");
Map(x => x.RowCreateDate, "ROW_CREATE_DATE");
Map(x => x.Description, "DESCRIPTION");
Map(x => x.ExistenceKdNm, "R_EXISTENCE_KD_NM");
References(x => x.FacilityClassItem, "FACILITY_CLASS_S").LazyLoad(Laziness.False).Fetch.Join().Not.Insert().Not.Update();
HasMany(x => x.FacilityCmList).KeyColumn("WHOLE_S").Fetch.Subselect().Not.LazyLoad();
}
When i try to do a SaveOrUpdate on the OtherFacility entity, it also updates all the entities in the FacilityCmList, which is fine, but in the last sql that is run tries to remove all relations between the parent and the child objects:
NHibernate.SQL: 2011-07-19 10:29:33,111 [361] DEBUG NHibernate.SQL [(null)] - UPDATE SOME.FACILITY_CMS SET WHOLE_S = null WHERE WHOLE_S = :p0;:p0 = '26021842'
I assume it has something to do with my mapping, any ideas?
After reading NHibernate sets Foreign Key in secondary update rather than on initial insert violating Not-Null Constrain on key column i found that i needed to add Inverse to the HasMany relation.
HasMany(x => x.FacilityCmList).KeyColumn("WHOLE_S").Fetch.Subselect().Inverse().Not.LazyLoad();

Categories

Resources