Basically what I am trying to achieve is I have three tables, one parent will always have an entry in it and only one of the other two tables will be populated. The complexity that I am dealing with is that the primary key for the tables is the combination of two fields
ParentTable
-----------
UniqueID
OwnerID
[Some more fields]
ChildTable1
-----------
UniqueID
OwnerID
[Some more fields]
ChildTable2
-----------
UniqueID
OwnerID
[Some more fields]
I was wondering if anyone has any suggestions on how best to do this through EF Code First preferably using the Fluent API.
You just need to define that the primary keys are composite...
modelBuilder.Entity<Parent>().HasKey(p => new { p.UniqueID, p.OwnerID });
modelBuilder.Entity<Child1>().HasKey(c => new { c.UniqueID, c.OwnerID });
modelBuilder.Entity<Child2>().HasKey(c => new { c.UniqueID, c.OwnerID });
...and then define the two one-to-one relationships:
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child1)
.WithRequired(); // or .WithRequired(c => c.Parent)
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child2)
.WithRequired(); // or .WithRequired(c => c.Parent)
You cannot define a constraint though (except probably by defining a trigger in the database) that would ensure that a given parent may only refer to one of the two children, but not to both. You must handle this restriction in your application's business logic.
Related
I use Fluent API to configure my unique constraints (EF 6.2) and have the following configuration:
modelBuilder.Entity<TagDTO>().HasIndex(p => new { p.SiteId, p.InputId, p.Name }).IsUnique();
modelBuilder.Entity<TagDTO>().HasIndex(p => new { p.SiteId, p.InputId, p.SqlColumn }).IsUnique();
This way I can configure a unique constraint using multiple columns. The Name and SqlColumn must be unqiue if the InputId and SiteId are the same.
I cannot configure these using attributes because of inhertitance in my model. Fluent API must be used to configure this.
Now the problem is the following: the geverated migration looks like this:
...
.Index(t => new { t.SiteId, t.InputId, t.Name }, unique: true)
.Index(t => t.SqlColumn, unique: true, name: "IX_SiteId_InputId_SqlColumn")
...
And when executing this migration only the Name is correctly configured in MS SQL. The SqlColumn does not have a unique constraint with multiple columns. When switching the two Fluent API lines the SqlColumnwill be correct and the Name incorrect.
Why is the index on SqlColumnnot configured with an object? Is it because of overlapping columns (SiteIdand InputId). Changing it manually in SQL works so it is possible.
Is this a bug in EF? Has anyone a solution for this problem?
SiteId: ForeignKey to table Site.
InputId: ForeignKey to table Input.
Name & SqlColumn: NOT primary key, just string columns
Thanks you.
EDIT: Changing the migration code manually does work but this cannot be a solution.
...
.Index(t => new { t.SiteId, t.InputId, t.Name }, unique: true)
.Index(t => new { t.SiteId, t.InputId, t.SqlColumn }, unique: true)
...
I have tables in the following structure.
Table1 Key, DocKey, Name
Table2 Key, DocKey, Name
Table3 Key, DocKey, Name
Table4 Key, DocKey, T1Key, T2Key, T3Key, Name, Value
I have defined the foreign key relation ship in the following way. What i am trying to do is, to make the T4 dependent and have navigational properties to T1, T2 and T3.
modelBuilder.Entity<Table4>()
.HasRequired<Table3>(d => d.Table3Nav)
.WithMany()
.HasForeignKey(k => new { k.DocKey, k.T3Key });
modelBuilder.Entity<Table4>()
.HasRequired<Table2>(d => d.Table2Nav)
.WithMany()
.HasForeignKey(k => new { k.DocKey, k.T2Key });
modelBuilder.Entity<Table4>()
.HasRequired<Table1>(d => d.Table1Nav)
.WithMany()
.HasForeignKey(k => new { k.DocKey, k.T1Key });
I am able to add objects, but when i am trying to save, i am getting the following error. tried searching through similar errors, but unable to figure out what need to change. Please assist.
Save
public int SaveDoc(Doc Document)
{
if (Document.Key == 0)
data.Docs.Add(Document);
else
{
var currDoc = data.Docs.Where(x => x.Key == Document.Key).FirstOrDefault();
data.Entry(currDoc).CurrentValues.SetValues(Document);
}
data.SaveChanges();
return Document.Key;
}
Referential integrity constraint violation. A Dependent Role has
multiple principals with different values.
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.
How do I create a composite UNIQUE constaint on 3 properties of a class?
It needs to allow NULL as a legitimate value.
This should be one of the ways to go about it..
mapper.Class<MyClass>(ca =>
{
ca.Property(x => x.Property1, map => map.UniqueKey("UQ_ComposedUniqueKey"));
ca.ManyToOne(x => x.FKField1, map => { map.UniqueKey("UQ_ComposedUniqueKey"); map.NotNullable(false); });
});
You can combine many properties or FKs in a single unique key.
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();