I use Entity Framework code first and I have problem because EF creates the Event table with two reference keys, one for class ObjectOne, and another for class ObjectTwo.
Is it possible to keep values of Id in one column Event.ObjectId and the type of relation in properties Event.Type?
I can do following mapping (in which I have only one column for FK). Is it possible to not create FK in database, because now I must add value that exists in ObjectOne table and ObjectTwo table and it's not a solution for me):
modelBuilder.Entity<ObjectOne>()
.HasMany(c => c.Events).WithOptional().HasForeignKey(x => x.ObjectId);
modelBuilder.Entity<ObjectTwo>()
.HasMany(c => c.Events).WithOptional().HasForeignKey(x => x.ObjectId);
Sample classes:
public class ObjectOne
{
public int ObjectOneId { get; set; }
public ICollection<Event> Events { get; set; }
}
public class ObjectTwo
{
public int ObjectTwoId { get; set; }
public ICollection<Event> Events { get; set; }
}
public class Event
{
public int EventId { get; set; }
public int ObjectId { get; set; }
public string Type { get; set; }
}
public class AddEvent : Event
{
}
public class Updated : Event
{
}
Thanks in advance
Such polymorphic associations are generally considered an anti pattern, see Bill Karwin's SQL Antipatterns. However, there may be situations where they get inevitable (or too practical to stay puritan). Bill Karwin is kind enough to recommend an implementation if you still want to use them. You should read the book for all the details but the heart of the matter is this diagram:
(where Comment has the role of your Event table, not to be confused with the Event table in the diagram!!).
In the base table all primary keys of tables that are associated with Event are collected.
With Entity Framework another approach is possible (although not really feasible with the number of tables you wish to accommodate): you can subclass Event in OneEvent, TwoEvent etc., where Type is the discriminator, and associate ObjectOne with OneEvents, ObjectTwo with TwoEvents, etc.
Related
I tried many different examples here, but i can't seem to figure out what i'm doing wrong.
I have a table with a history table to it. I have removed many of the fields to make it easier to watch. After my migration it works fine if i watch in PHPMyAdmin and watch the primaryKey there.
I want to be able to go context.ProductArtifactDocumentState.Histories so i can get all linked histories.
DBContext
//Composite Key
builder.Entity<ProductArtifactDocumentStateHistory>()
.HasKey(k => new { k.Version, k.ProductArtifactDocumentStateId});
Table
[Table("ProductArtifactDocumentsState")]
public partial class ProductArtifactDocumentState : BaseEntity
{
public virtual ICollection<ProductArtifactDocumentStateHistory> ProductArtifactDocumentStateHistories { get; set; }
}
History Table
[Table("ProductArtifactDocumentsState_History")]
public partial class ProductArtifactDocumentStateHistory
{
[Column("ProductArtifactDocumentStateId")]
public int ProductArtifactDocumentStateId { get; set; }
public virtual ProductArtifactDocumentState ProductArtifactDocumentState { get; set; }
public int Version { get; set; }
}
The error i get:
System.InvalidOperationException: 'The entity type 'ProductArtifactDocumentStateHistory' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.'
Why do you need all this headache? Are you trying to save 2 bytes on a primary key? But after this all your ef code will be a nightmare. Just add Id field
public partial class ProductArtifactDocumentStateHistory
{
[Key]
public int Id {get; set;}
.....
}
All,
Is it possible to use the same FK for two tables.
Probably it is not a good practice, but I have a two different classes that can be both booked:
public class Course {
public Course() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Title { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
public class GiftCard {
public GiftCard() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Prop1 { get; set; }
public int Prop2 { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
// this is the bookin reference for a Course or an GiftCard
public class BookingRef {
public BookingRef() {
}
public long Id { get; set; }
// other props ...
/// <summary>The item (usually the course but theoretically anything with a long id)</summary>
public long? ItemId { get; set; }
// maybe a generic Object?
[ForeignKey(nameof(ItemId))]
public Object GiftCard { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public Course Course { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public GiftCard GiftCard { get; set; }
}
Is it possible to use the same FK for two tables
No. The relational model doesn't allow that. You can introduce a superclass of all your bookable things and have a FK to that, but you shouldn't do that just get a single collection rather than multiple.
Think of it from the relational data perspective. How would the database know what table an "Item ID" pointed at? How would it index it?
This would be a case for using a null-able FK to each related table on the booking. These FKs do not need to reside in the entity, just the navigation properties. You can leverage .Map(x => x.MapKey) in EF6 or .HasForeignKey("") in EF Core to leverage a shadow property.
This does not enforce if you want a booking to only be associated to a course or a gift card but not both. That would need to be catered for at the application level, and I would recommend using a scheduled maintenance task to evaluate the data for violations to that rule. (Look for bookings holding both a course ID and a gift card ID for example)
You can alternatively keep the joins "loose" and evaluated by the application based on a discriminator similar to an inheritance model. (ItemId + ItemType) However you have to resolve the relationship load separately in your application based on the ItemType and lose out on any FK, indexing, and data integrity checks in the database. This could be a significant performance & maintenance cost to save adding a couple FKs.
public class Product
{
public string Name { get; set; }
public int Qty { get; set; }
public decimal Price { get; set; }``
}
public class CartItem : Product
{
public int CartItemId { get; set; }
public string CartId { get; set; }
}
public class OrderLine : Product
{
public int OrderLineId { get; set; }
public int OrderId { get; set; }
}
public class Kititem : Product
{
public int KititemId { get; set; }
public int OrderId { get; set; }
}
public class SampleContext : DbContext
{
public DbSet<CartItem> CartItems { get; set; }
public DbSet<OrderLine> OrderLines { get; set; }
public DbSet<Kititem> Kititems { get; set; }
}
As you can see in this I am not including the parent class Product in the DbContext, and when doing the migrations it creates a table for each derived class with all the properties form the parent class, and it does not create the parent class because is not included in the Dbcontext, for me it was what I was exptecting and is working, and I am happy with it so far
Mi question is if that is a bad practice and maybe I am not using ef core Inheritance the way I can take all the advantages ?
I am confuse and want to start a new project and do it the best way, so I don't have to redo it again
What you are using is called "base class" as opposed to "base entity", i.e. class participating in database model inheritance.
You are not forced to use database inheritance at all. Moreover EF Core currently supports only Table per Hierarchy (TPH) strategy, which is not the best if you have many derived entities with many different properties (because all the data is stored in a single table).
In other words, there is nothing wrong to not use database inheritance. The only benefit of database inheritance is if you need polymorphic queries, i.e. queries that return Products of different types. It's possible to do such queries w/o database inheritance using Union / Concat LINQ operator, but they won't be efficient due to current EF Core lack of translating such queries to SQL, so they always use client evaluation.
Of course this will be improved in some future EF Core version (as well as support for other inheritance strategies), so the main question should be - do you need such queries. If you don't, then your approach of not using database inheritance is just fine. If you do, then decide which is more suitable for your needs - manual Concat or a single table with a lot of columns.
I'm trying to solve one puzzle, but with no luck so far.
I have an article (or blog post) and comment entities, they both have content. In order to support lazy loading for content (there is no need to load the content when I need to display a list of articles or comments) I decided to move content to separate table and organize one-to-one mapping. Here is an example of what I think:
public class Content {
[Key]
public int ID { get; set; }
public string RawContent { get; set; }
// a bunch of scalar properties, like content type and so on
}
public class BlogArticle {
[Key]
public int ID { get; set; }
public int ContentID { get; set; }
[ForeignKey(nameof(ContentID)]
public virtual Content Text { get; set; }
// other properties related to BlogArticle
}
public class Comment {
[Key]
public int ID { get; set; }
public int ContentID { get; set; }
[ForeignKey(nameof(ContentID)]
public virtual Content Text { get; set; }
// other properties related to comment
}
<...>
From first look it seems ok: I can create blog articles, comments and attach content (at first I insert content, obviously). Update works as well. However, deletion doesn't work: when I delete blog article or comment, content is not deleted (but I want to delete it when blog article or comment are deleted, not opposite).
From what I understand my biggest issue because of relationship direction: in my case, Content entity is principal end and BlogArticle and Comment are dependent ends. In order to solve the puzzle, I need to change principal/dependent relationship. Again, from what I understand in order to change relationship direction I need to have a foreign key in Content entity and use fluent API to describe who is parent (principal) and who is child (dependent) in one-to-one relationship. Since many tables (there might be other entities with content property) are pointing to Content table, it doesn't seem very easy. Am I correct in my understanding?
One possible solution I could imagine is to create multiple foreign keys in Content table and point to each related table:
public class Content {
[Key]
public int ID { get; set; }
public string RawContent { get; set; }
// foreign keys
public int BlogArticleID { get; set; }
public int CommentID { get; set; }
public int WebWidgetID { get; set; }
// other foreign keys if necessary
}
probably, foreign keys must be nullable (because only single foreign key is used at once). Then use Entity Framework fluent API to describe relationship directions and organize cascade delete. For me it looks ugly, but I have no other ideas.
My question: is my proposed solution good/reliable? Are there other options I can look at?
Thanks in advance!
All your thoughts are correct. And your proposed solution is the only way with traditional relational design. The drawback of course is the need of multiple mutually exclusive nullable FKs.
The other options I see are as follows:
(1) Using EF inheritance for the entities holding Content. e.g.
public abstract class EntityWithContent
{
[Key]
public int ID { get; set; }
public virtual Content Text { get; set; }
}
public class BlogArticle : EntityWithContent
{
// other specific properties
}
public class Comment : EntityWithContent
{
// other specific properties
}
and configured one-to-one relationship between Content (dependent) and EntityWithContent (principal) using either shared PK association or FK association.
But since EF Core currently supports only TPH strategy (i.e. all the derived entities share one and the same table with union of all fields), I won't recommend it.
(2) Making Content owned type.
This is closer to the intent, but unfortunately EF Core currently always loads the owned entity data along with the owner data (even if they are configured to be provided by different database tables), which is against your original goal, so I won't suggest that either.
(3) Using table splitting feature.
If the main goal is simple to support controlled (lazy/eager/explicit) loading and the Content is always required, then this might be the best solution so far.
It would require a bit more configuration, but at the end it will give you the original table design (single table per entity) with the desired loading behavior:
Model:
public abstract class Content
{
[Key]
public int ID { get; set; }
public string RawContent { get; set; }
// a bunch of scalar properties, like content type and so on
}
public class BlogArticle
{
[Key]
public int ID { get; set; }
public virtual BlogArticleContent Text { get; set; }
// other properties related to BlogArticle
}
public class BlogArticleContent : Content
{
}
public class Comment
{
[Key]
public int ID { get; set; }
public virtual CommentContent Text { get; set; }
// other properties related to comment
}
public class CommentContent : Content
{
}
Note that here Content class is not part of EF inheritance hierarchy, but simple base class with the common properties (abstract modifier is not strongly necessary). The actual derived classes might or might not define their own properties.
Configuration:
modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
modelBuilder.Entity<BlogArticle>()
.HasOne(e => e.Text)
.WithOne()
.HasForeignKey<BlogArticleContent>(e => e.ID);
modelBuilder.Entity<BlogArticleContent>().ToTable("BlogArticles");
modelBuilder.Entity<Comment>().ToTable("Comments");
modelBuilder.Entity<Comment>()
.HasOne(e => e.Text)
.WithOne()
.HasForeignKey<CommentContent>(e => e.ID);
modelBuilder.Entity<CommentContent>().ToTable("Comments");
I am interested in how I can map two entities to same table, by using code first. Here's an example:
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public byte Age { get; set; }
public bool Active { get; set; }
public DateTime Created { get; set; }
}
public class UserViewModel
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public byte Age { get; set; }
}
Basically I'm fed up with building repositories. I want to map all possible models for configuration portal, user portal, other services in modelbuilder and just use DbContext for everything. I want to set User class as top of the hierarchy and a class that builds the database, while all other models should just be there for various applications.
I don't want to use automapper. I've also done fair amount of manual coding which just wasted my time, and every single modification requires me to go back to repository and recode - which annoys me.
I've tried to use this in modelbuilder, but it warns me that hierarchy is not valid:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Map(p => { p.ToTable("Users"); });
modelBuilder.Entity<UserViewModel>().Map(p => { p.ToTable("Users"); });
}
Also keep in mind that I'm not trying to achieve "Table splitting". I don't want my table to be split in two entities, I want rather to have all columns nullable, except one with primary key, and allow different applications/web services/web portals to populate as much data as they've been granted access for.
Thanks for all the tips :)
You can't. One table = one entity (except advanced mappings like mentioned table splitting and TPH inheritance). View model is not and entity. It is just view on data / projection so handle it that way. You will always work with User and project user to view model you need:
var view = from u in context.Users
select new UserViewModel
{
UserId = u.UserId,
Name = u.Name,
Age = u.Age
};
Make this as reusable method returning IQueryable<UserViewModel> and you can do whatever you want.
Table Per Hierarchy TPH inheritance in entity framework with code first
https://www.youtube.com/watch?v=2i7jahkpeQ8&list=PL6n9fhu94yhUPBSX-E2aJCnCR3-_6zBZx&index=19