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
Related
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.
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");
In an MVC4 web application using the Razor engine and entity framework, is it possible to create a template for use with the html helper #Html.EditorForModel , so that entities with links to other tables are better displayed.
The example I am working with is a DbContext containing two DBSets, Regions and Schools. There are many regions, and a school may belong to one region. Ideally I would like the editor for schools to show a dropdown of regions to select from. I would like to make a template that is generic enough so that I can just call the #Html.EditorForModel helper and the form is generated in one go, and that I could make changes to the region or schools tables later on and for the changes to be reflected in the edit form without me needing to make alterations.
Some code:
public class MyContext : DbContext
{
public MyContext ()
: base("DefaultConnection")
{
}
public DbSet<Region> Regions { get; set; }
public DbSet<School> Schools { get; set; }
[Table("Regions")]
public class Region
{
public Region()
{
Schools = new List<School>();
}
[Key]
public int RegionId { get; set; }
[StringLength(256)]
public string RegionName { get; set; }
public ICollection<School> Schools { get; set; }
}
[Table("Schools")]
public class School
{
public School() { }
[Key]
public int SchoolId { get; set; }
[StringLength(256)]
public string SchoolName { get; set; }
[ForeignKey("Region")]
public int RegionId { get; set; }
public virtual Region Region { get; set; }
}
}
I have created a partial view to create display the editor form, with the idea that I can pass in either a Region or a School as the view model.
#model object
#using (Html.BeginForm())
{
#Html.ValidationSummary("Broken stuff:")
#Html.EditorForModel()
}
I don't know if this is possible, but I would really like the new template to loop over the properties of the entity and detect if there is a linked table (e.g. School contains a Region) and display the list of regions in a dropdown.
Well, in general, it's a bad idea to pass your data models directly to your views for rendering. There are security issues there, among other things. But, even if you are going to do this you would not want to pass your entire data context like that because it's not structured in a way that is suitable for rendering in a web page.
Off corse you can. Look at here to see how.
An alternation is that you use scaffolding and let it to create your views. Most of times(!) it creates correct DropDowns for you and you just customize your view if you want.
And in those few situations that yo see strange DDLs, you just need to correct ValueName and DataName parameters in the related action methods whic return list items to the view...
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.
I have taken a model first approach for a project i'm working on. An example of a class relationship is shown as follows, pretty strightforward:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
List<Photo> Photos { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Path { get; set; }
}
The database schema will roughly be:
--------------
Products Table
--------------
Id int,
Name Varchar
------------
Photos Table
------------
Id int,
Path varchar
ProductId int FK Products.ID
A Product can have Zero or more Photos.
Now when i try to plug is my ORM of choice (Entity Framework V4 - Poco approach) iam forced to map my relationships in the domain model!
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
List<Photo> Photos { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Path { get; set; }
public int ProductId {get; set; } //Foriegn Key
public Product Proudct {get; set; } //For uni-directional navigation
}
Firstly, i dont need/want uni-directional navigation. I understand this can be deleted. Secondly, I dont want the Foriegn Key declared in the Photos class.
I dont think this is true POCO/persistence ignorance if i must define database properties in the Domain Objects?
Do other ORM's behave this way?
I found the answer. Using the wizard, there is an option to "Include foreign key columns in the model" - Uncheck this box and you will a clean conceptual model without FK.
Make sure Code Generation Strategy is set to none in the properties window.
Why don't you want to have Photo.Product property? If there is no such property, it seems one photo can belong to several products and since database schema should be more complex (with auxiliary table).
The relationships don't have to be two-way, and don't have to be public (if you use true POCOs, not proxy types). You've said quite a bit about what you don't want in your code, but can you be clearer about how you do want to define the relationships? It has to go somewhere. Where would you like to put it? There are many options.