EF abstract base class with key where derived class has different key - c#

Time for a dumb question. I think the database design is screwy, but there isn't much I can do about that part of it. I have a table in the database "Table1" and then "Table2" which is essentially an extension of Table1 (I also have Table3, Table4, etc). My problem is that Table2 has it's own unique key, even though it's a one for one relationship. Then Table2Component uses Table2Id as it's foreign key. However, when I try to use that in my code I think it's pointing to Table1Id. I get the error message:
System.Data.Entity.Edm.EdmAssociationConstraint: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'Table2Id' on entity 'Table2Component' does not match the type of property 'Table1Id' on entity 'Table2' in the referential constraint 'Table2Component_Table2'.
Here is the code
[Table("Table1")]
public abstract class Table1
{
[Key]
[Column("table1_id")]
public string Table1Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("type_cd")]
public string TypeCode { get; set; }
}
[Table("Table2")]
public class Table2 : Table1
{
[Key]
[Column("table2_id")]
public int Table2Id { get; set; }
[ForeignKey("Table1Id")]
public virtual Table1 Table1 { get; set; }
// this table also has a table1_id column
// but I guess I don't need it here, correct?
[Column("column1")]
public string Column1 { get; set; }
public virtual ICollection<Table2Component> Table2Components { get; set; }
}
[Table("Table2Component")]
public class Table2Component : ISubItem
{
[Key]
[Column("table2_component_id")]
public int Table2ComponentId { get; set; }
[Column("table2_id")]
public int Table2Id { get; set; }
[Column("description")]
public string Description { get; set; }
public bool Required { get { return true; } }
[ForeignKey("Table2Id")]
public virtual Table2 Table2 { get; set; }
}
Any suggestions? Should I be more forceful in trying to get the database changed?

Started as comment.... finish as simple answer, since no one else jumped in.
Search for Entity Framework 1:1 relationship eg https://stackoverflow.com/a/14997417/1347784 the restriction is both tables must have the same foreign key when using 1:1
No not necessarily better database design. It is Just the why the EF team built the framework. Ive learnt to live with the restrictions. In code first scenario, no big deal. Try the powertool to reverse engineer the alternative approach when you start with the DB. EF will use 1:M even though you may see it as 1:1. Also OK in my view.

Related

EF foreign key reference using Id vs object

What is the difference between foreign key reference using Id vs object.
For example:
FK relation using Id
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public int CategoryId { get; set; }
}
vs
FK relation using object
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public Category Category { get; set; }
}
I have noticed from database that by using Id property the column essentially becomes non-null field.
Is that the only difference?
I think we can't query multiple tables at once i.e.querying related data?
When should I choose to use either options?
In your first example you're not adding a relationship, just an integer property named CategoryId.
In your second example, Entity Framework will create an integer column named "Category_ID", but you will be not be able to see this property in your model, so I like to explicitly add it my self and be able to use it along with the navigation property.
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category{get;set;}
}
This way you can also control the data type of CategoryId, so you could make it optional (nullable)
public int? CategoryId { get; set; }
*The foreign key data annotation is not needed, unless you have property or navigation property names that do not follow the naming convention for foreign key property names (Bardr), it doesn't harm to explicitly declare it either for clarity purposes
This implies that you're creating a 1 to many relationship (1-*) with products and categories, so in your Category class you would be adding a collection navigation property for products
class Category
{
public int Id{ get; set;}
public string Name{ get; set; }
...
public ICollection<Product> Products{get; set;}
}
Basically it depends on your use case and what type of loading related data you choose. Whether you use Id or object reference or full relationship on both sides (Id and object) it depends on your overall application architecture. If you wil go and use full or object reference everywhere, you will (probably) end up with a mess, and you won't know whether you should query for some entities using their repository or if it'll be okay to include them to some other query. I highly recommend you to take a look at this book, especially chapter 19 (Aggregates) and 21 (Repositories). There you have an in-depth explanation of what I meant and much more. (This does not only apply to applications built in DDD way)

EF6 Many-to-Many on Non-Key Column

I am having trouble connecting a many-to-many relationship in EF6 using code first, then creating a pass-through association beyond that.
There are three classes: Person, Tag, and Passing.
Each Person has an optional Bib.
Each Tag has an optional Bib, not unique.
Each Passing has a required TagId.
I want to access all Passings linked to a Person by getting all Tags with the same Bib, then getting all Passings associated with each of those Tags.
I have tried using the DBModelBuilder in my DBContext class but can't get it to work properly, and EF6 seems to try to generate an intermediate table anyways, which seems unnecessary.
public class Person
{
[Key]
public int PersonId { get; set; }
...
public string Bib { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual ICollection<Passing> Passings
}
public class Tag
{
[Key]
public string TagId { get; set; }
public string Bib { get; set; }
public virtual ICollection<Passing> Passings { get; set; }
public virtual Person Person { get; set; }
}
public class Passing
{
[Key]
public int PassingId { get; set; }
...
public string TagId { get; set; }
public virtual Tag Tag { get; set; }
}
IT IS necessary, when you have a * to * multiplicity into a table, it automaticly creates another table that links these, elseway you cannot put an infinite and variable number of foraign key in one of your table
Entity Framework uses navigation properties to represent database relationships. If you don't want an additional table, what you have here is not a database relationship since keys aren't involved.
You should be able to use some kind of function (or extension function) to get what you want:
IQueryable<Passing> PersonPassings(YourContext db, Person p)
{
return db.Passings.Where(pa => pa.Tag.Bib == p.Bib);
}
On the other hand, if you want to create a proper relationship, you'll need an intermediate Bibs table to connect Person and Tag.

How to set up Navigation Properties to enforce deleting related records

I am using MVC 4 and VS2010 with Entity Framework 6.1.x. I am working with a Code First fairly easy database but there is a slightly more complex part of it. First of all, two tables Person and Recording have relationship 1 to many.
public class Person {
public int PersonID { get; set; }
public int GenderID { get; set; }
// Navigation property
public virtual List<Recording> Recordings { get; set; }
}
public class Recording {
public int RecordingID { get; set; }
// ...
public int PersonID { get; set; }
// Navigation properties
public virtual Person Person { get; set; }
public virtual List<Junction> Junctions { get; set; }
}
By default, when I delete a Person, Entity Framework deletes all recordings related to Person. That's what I expect. But the records from Recording table are also placed in different set of tables. Let's say I have table called Applicant, Application and Junction. When I delete any recording either by deleting Person or Recording, I would like all records in Junction which are related to records in Recording to be deleted, as well. In my project it wouldn't make sense if there are zero number of recordings associated with particular application.
There is a combined Primary Key, that is ApplicantID, ApplicationID, and RecordID in Junction make the complex Key.
Is it possible to enforce constraints via Entity Framework or I'd rather have to provide my own custom function?
How to set up Navigation properties in corresponding tables: Recording and Junction so that related recordings are deleted?
public class Applicant
{
public int ApplicantID { get; set; }
// Navigation property
public virtual List<Junction> Junctions { get; set; }
}
public class Application {
public int ApplicationID { get; set; }
// Navigation property
public virtual List<Junction> Junctions { get; set; }
}
public class Junction
{
public int ApplicationID { get; set; }
public int ApplicantID { get; set; }
public int RecordingID { get; set; }
public virtual Application Application { get; set; }
public virtual Applicant Applicant { get; set; }
public virtual Recording Recording { get; set; }
}
Thanks for any help.
EDIT
#Chris. From what I have understood, if a Foreign Key is not nullable, records in the secondary table are deleted if a record in the Primary table with the corresponding PrimaryID are deleted. On the other hand, if the Foreign Key is nullable, records in the secondary table, would become null but not deleted.
I made this project and two tables and I filled the database. In the class Student, if I leave DepartmentID not nullable,
public int DepartmentID { get; set; }
records are deleted when a record with primary DepartmentID is deleted. This is what I expect but if I make DepartmentID nullable
public int? DepartmentID { get; set; }
then I've got this error:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Student_dbo.Department_DepartmentID". The conflict occurred in database "TestDB", table "dbo.Student", column 'DepartmentID'.
The statement has been terminated.
In test project I made two tables with relationship 1 to many.
public class Department
{
public int DepartmentID { get; set; }
public string DepartmentName { get; set; }
public virtual List<Student> Students { get; set; }
}
and
public partial class Student : IDisposable
{
public int StudentID { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
Is this what I should expect?
The delete cascade is not intended to just delete items on a whim. It exists out of necessity. In your example above, Recording has a non-nullable foreign key to Person. If that Person is deleted, the foreign key would either have to be set to null (which can't happen) or all related Recordings must be deleted to preserve referential integrity.
In the second example with Junction, there's a non-nullable foreign key to Record. So if you delete a Person all related Records and all Junctions related to those records should be deleted. However, if you delete a Junction no further action is taken. There is nothing inherently dependent on a Junction so its deletion goes unnoticed. Any Records it once referenced are still valid. Remember this is all about maintaining referential integrity. As long as integrity is intact, nothing will ever be deleted.

Determining if a model should have foreign keys / navigation properties

I'm building a fairly simple MVC project and still getting my head around where to use navigation properties and foreign keys with code first.
This is the main model class:
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
public GroceryCategory Category { get; set; }
public QualityProfile Quality { get; set; }
public GroceryStore BestStore { get; set; }
public double BestPrice { get; set; }
public double LastSeenPrice { get; set; }
//Navigation Properties
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
}
and these are the relating classes:
public class GroceryStore
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Uri Website { get; set; }
}
public class QualityProfile
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
/// <summary>
/// Rank out of 1-10, 10 being the best
/// </summary>
public byte Ranking { get; set; }
}
public class GroceryCategory
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Which brings me to my question, is the navigation property of SimilarItems I have in the GroceryItem class sufficient to represent a list of multiple grocery items or does this not work as it is referring to itself?
Additionally...do the Category, Quality and BestStore properties require ID properties to represent a foreign key inside of the GroceryItem class (e.g. CategoryID), or is the way I have this represented OK?
----EDIT----
--Refactored Code--
I've re-factored my model based on the suggestions below, which I think better accommodates the suggestions you've made (yes a 2nd time), realised my model was a little flawed and extracted out the price component into a separate purchases Model.
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
[ForeignKey("Quality")]
public int QualityID { get; set; }
//Navigation Properties
public virtual QualityProfile Quality { get; set; }
public virtual GroceryCategory Category { get; set; }
}
However the last thing I'm uncertain about which is on topic to this post, is if I have a collection as a part of the model (one that does not reference itself like in the first example), can I just represent that with a navigation property or does an extra step need to be taken?
Ie. If I was to allow multiple different categories on a GroceryItem, instead of looking like this:
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual GroceryCategory Category { get; set; }
it would look like this:
public virtual ICollection<GroceryCategory> Categories { get; set; }
The best answer to your question(s) is, "It depends". Navigation properties are one way of informing Entity Framework that there's a relationship between entities. By convention, if you have a navigation property such as:
public Category Category { get; set; }
Entity Framework will create a column on the table named in the form of [RelatedPropertyName]_[RelatedPK]. Given your classes, the property above would cause a column named Category_ID. There's nothing more you need to do make it work. The relationship will automatically be handled by EF.
However, doing it this way, you won't have access to this foreign key property. It's not exposed in the public API of your entity. Often, especially when selecting related items from a select list and similar such scenarios, this becomes problematic, as you must store the selected value some place else, usually a property on a view model, and then use this to query the related thing from the database before setting it on the entity it belongs to and finally saving the entity. Whereas, with an actual foreign key property, you can simply post directly back to this and Entity Framework will automatically wire up the related entity. As a result, I tend to always follow the following pattern with my navigation properties:
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
In most scenarios, Entity Framework will automatically connect those two, such that FooId will hold the foreign key relationship for the Foo navigation property. However, occasionally, EF will trip up and try to create the implicit foreign key behind the scenes, still, but you can correct that behavior by explicitly telling EF that this is the foreign key:
[ForeignKey("Foo")]
public int FooId { get; set; }
Roughly the same applies with collection navigation properties. EF will see this as an indication that there's a one-to-many relationship in play and add the implicit foreign key on the opposite entity. Given your collection:
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
The opposite entity is actually the same entity, which presents an interesting use case. Typically, EF would handle this by assuming there's a one-to-many relationship. You'd end up with a column named GroceryItem_ID on your dbo.GroceryItems table. Here, though, you would not only have no access to the foreign key directly, but you also have no public API for accessing the parent GroceryItem either. That may not be a problem, but it's something to be aware of. The only way you'd be able to manage the relationship is through the collection on the parent, not through a child item in that collection.
However, since this is self-referential and you have not specify a foreign key or instance navigation property, all EF will see is a collection on both sides of the relationship, so my guess is that you'll actually end up with an M2M with an intermediary table. I can't test that theory out myself at the moment, and I haven't tried this particular scenario myself previously.
To create a true one-to-many, you would need to create another navigation property similar to:
public virtual GroceryItem ParentGroceryItem { get; set; }
And, even, then, I don't think EF will get the point without a little Fluent configuration:
HasMany(m => m.SimilarItems).WithOptional(m => m.ParentGroceryItem);
You could also use WithRequired in other scenarios instead of WithOptional, which would obviously make the relationship a required one, but since this is self-referential, it's impossible to have it required, because there will have to be at least one root node with no parent.

One to many relation to one table causes an error in EF modeling

I create a model in EF6 that contains several tables one of my tables is ContentGrouptable with this structure :
public partial class ContentGroup
{
public ContentGroup()
{
this.Contents = new HashSet<Content>();
this.ContentGroups = new HashSet<ContentGroup>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Visible { get; set; }
public long Ordering { get; set; }
public string CommentState { get; set; }
public int ContentGroupId { get; set; }
public virtual ICollection<Content> Contents { get; set; }
public virtual ICollection<ContentGroup> ContentGroups { get; set; }
public virtual ContentGroup ContentGroup1 { get; set; }
}
As you can see this table has a relation whit itself .One to many on Id column.It this table we have a FK-ContentGroupId that refers to itself(ContentGroup).So i create a view for this entity using MVC4(ASP) so when i want to insert a value to my table i got this error:
"Unable to determine a valid ordering for dependent operations. Dependencies may
exist due to foreign key constraints, model requirements, or store-generated
values."
So what should i do ?it because of FK?
Best regards
The problem is that you have a non-nullable ContentGroupId. This means that when you insert a ContentGroup, you have to set its parent ContentGroup as well. But this a chicken and egg problem (or: no "valid ordering"). You can't insert a parent and child at the same time, it's always a sequential operation. And then, there must always be a "mother of all parents", not having a parent itself.
So, make ContentGroupId nullable and map the associations like so:
modelBuilder.Entity<ContentGroup>()
.HasOptional(cg => cg.ContentGroup1)
.WithMany(cg => cg.ContentGroups)
.HasForeignKey(cg => cg.ContentGroupId);

Categories

Resources