I am learning EF and unclear when to create a DbSet for an entity. In the standard Blog example that the Microsoft Docs have, they define DbSets for both Blog and Post. Why would one need to create a DbSet for Posts? Isn't the Posts accessible thru Blogs and therefore I can get the Posts using a LINQ query?
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Why would one need to create a DbSet for Posts?
It's not strictly necessary. The rules in EF Core are:
By convention, types that are exposed in DbSet properties on your
context are included in your model. In addition, types that are
mentioned in the OnModelCreating method are also included. Finally,
any types that are found by recursively exploring the navigation
properties of discovered types are also included in the model.
Including & Excluding Types
But common practice is to declare a DbSet for each Entity because you might want to write a query that starts with Posts, or retrieve a single Post without its associated Blog.
Related
I am using VS2015 and have MVC5 web app. I want to use scaffolding feature to generate CRUD for my child entities: currently when generating the scaffolding it is NOT creating the views/edit/create for the IEnumerable collection 'Cities'.
I have googled but not found anything. Is there a Nuget package that does what I want. It should allow to add/delete/edit the cities maybe using partial view but should be auto-generated.
code:
public partial class Country
{
public int ID { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public string Location { get; set; }
public virtual IEnumerable<Cities> Cities { get; set; }
}
public partial class Cities
{
public int ID { get; set; }
public string Name { get; set; }
public string TreLocation { get; set; }
public string Geo { get; set; }
}
Do you want an editable list of cities under each country?
Don't use mvc! In the time you spend figuring it out, you can probably learn angular. Which is far better for tis sort of thing.
However, back in the mists of time wise sages wrestled with this problem, for example....
Collection of complex child objects in Asp.Net MVC 3 application?
https://www.donnfelker.com/editable-grid-list-binding-in-mvc2/&ved=2ahUKEwiltefd9cngAhWjQxUIHe45AM4QFjACegQIARAB&usg=AOvVaw2z8iH5hZnObcnIMl3d4cyI
The are plenty of resources describing G11n an L10n in aspnetcore including the official docs.
But are there any libraries that simplify the implementation of a user content localization? An example could be a content of a blog post that may be translated into multiple languages. Such a library would use a specific table in SQL for storing/retrieving translation.
Here is the possible use case:
// this object contains content that user can add manually
public class BlogPost
{
// should be localised
public string Content { get; private set; }
}
It seems like we can add a collection of "string Content" in order to solve this issue:
public class LocalizableContent
{
public string CultureInfo { get; private set; }
public string Content { get; private set; }
}
public class BlogPost
{
public ICollection<LocalizableContent> Content { get; private set; }
}
Note: after making a bit of googling I found a related question (but it gives no answers):
Best Practices to localize entities with EF Code first | StackOverflow
Also, it doesn't seem like this library can help:
github.com/damienbod/AspNetCoreLocalization
Any suggestions?
I think you can implement your content localization library without the need to use third parties. Personaly I do something like below:
public class BlogPost {
public int Id { get; set; }
public ICollection<BloPostLocalized> Localizations { get; set; }
}
Create a localized class for BlogPost:
public class BlogPostLocalized {
public int Id { get; set; }
public BlogPostId { get; set; }
public BlogPost { get; set; }
public string Culture { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
Notice that the main BloPost has no Title nor Content fields, because we will have them defined in the BlogPostLocalized class for neutral and localized cultures.
So each blog post will have multiple localized versions that can be fetched simply from the db as a child of the main post.
Is it possible to do foreign key without blablaId?
I mean next situation
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; } // Why should I have this ugly property?
public virtual Blog Blog { get; set; }
}
My models are core project. But I should play EF rules. It means core project depend on DAL project.
It's not required declare the FK property in your entity. If you delete it, EF will create it for you in your DB.
However, imagine if the Blog is not in memory, this would require you to first execute a query on the database to retrieve that Blog so that you can set the property. There are times when you may not have the object in memory, but you do have access to that object’s key value. With a foreign key property, you can simply use the key value without depending on having that instance in memory:
currentPost.BlogId=2;
I am getting Error when trying to run this code.
Unable to determine the principal end of an association between the
types 'AddressBook.DAL.Models.User' and 'AddressBook.DAL.Models.User'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
The objective is that i am creating baseClass that has commonfield for all the tables.
IF i don't use base class everything works fine.
namespace AddressBook.DAL.Models
{
public class BaseTable
{
[Required]
public DateTime DateCreated { get; set; }
[Required]
public DateTime DateLastUpdatedOn { get; set; }
[Required]
public virtual int CreatedByUserId { get; set; }
[ForeignKey("CreatedByUserId")]
public virtual User CreatedByUser { get; set; }
[Required]
public virtual int UpdatedByUserId { get; set; }
[ForeignKey("UpdatedByUserId")]
public virtual User UpdatedByUser { get; set; }
[Required]
public RowStatus RowStatus { get; set; }
}
public enum RowStatus
{
NewlyCreated,
Modified,
Deleted
}
}
namespace AddressBook.DAL.Models
{
public class User : BaseTable
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string Password { get; set; }
}
}
You need to provide mapping information to EF. The following article describes code-first strategies for different EF entity inheritance models (table-per-type, table-per-hierarchy, etc.). Not all the scenarios are directly what you are doing here, but pay attention to the mapping code because that's what you need to consider (and it's good info in case you want to use inheritance for other scenarios). Note that inheritance does have limitations and costs when it comes to ORMs, particularly with polymorphic associations (which makes the TPC scenario somewhat difficult to manage). http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx
The other way EF can handle this kind of scenario is by aggregating a complex type into a "fake" compositional relationship. In other words, even though your audit fields are part of some transactional entity table, you can split them out into a common complex type which can be associated to any other entity that contains those same fields. The difference here is that you'd actually be encapsulting those fields into another type. So for example, if you moved your audit fields into an "Audit" complext type, you would have something like:
User.Audit.DateCreated
instead of
User.DateCreated
In any case, you still need to provide the appropriate mapping information.
This article here explains how to do this: http://weblogs.asp.net/manavi/archive/2010/12/11/entity-association-mapping-with-code-first-part-1-one-to-one-associations.aspx
Spinning further on the previous question I had. Let's say I inherit BlogEntry and Comment from Post. I now want to customize them a bit. A comment to a blog post does not need a title but a comment needs a user reference so I move these two fields out from Post and into Comment and Blog entry like this:
public abstract class Post
{
public virtual int Id { get; set; }
public virtual string Text { get; set; }
public virtual DateTime CreatedAt { get; set; }
}
public class BlogEntry : Post
{
public virtual string Header { get; set; }
public virtual Blog Blog { get; set; }
public virtual IEnumerable<Comment> Comments { get; set; }
}
public class Comment : Post
{
public virtual string Header { get; set; }
public virtual int UserId { get; set; }
public virtual BlogEntry BlogEntry { get; set; }
}
Now I create my custom object context:
public class EntityContext : System.Data.Objects.ObjectContext
{
public EntityContext() : base("name=Entities", "Entities")
{
this.Blogs = CreateObjectSet<Blog>();
this.Posts = CreateObjectSet<Post>();
this.Entries = CreateObjectSet<BlogEntry>();
this.Comments = CreateObjectSet<Comment>();
}
public ObjectSet<Blog> Blogs { get; set; }
public ObjectSet<Post> Posts { get; set; }
public ObjectSet<BlogEntry> Entries { get; set; }
public ObjectSet<Comment> Comments { get; set; }
}
This gives me the following actually quite descriptive error message:
Test method threw exception:
System.ArgumentException: There are
no EntitySets defined for the
specified entity type 'BlogEntry'. If
'BlogEntry' is a derived type, use the
base type instead. For example, you
would see this error if you called
CreateObjectSet()
and DiscontinuedProduct is a known
entity type but is not directly mapped
to an EntitySet. The
DiscontinuedProduct type may be a
derived type where the parent type is
mapped to the EntitySet or the
DiscontinuedProduct type might not be
mapped to an EntitySet at all.
Parameter name: TEntity
Now I am not a master of inheritance and stuff but the way I see this would be to add a set of Entries and Comments as ObjectSet< Post> that would solve my problems?
If you create an ObjectSet for a base type (i.e. Post) you can't create one for derived types too, because you can retrieve instances of all types in the inheritance hierarchy from that one ObjectSet.
i.e. ctx.Posts.OfType<BlogEntry>() would return BlogEntry(s).
So the answer is to simply remove the other two sets.
If yo need to do an add for example you can do this:
ctx.Posts.AddObject(new BlogEntry {....});
no problem at all.
To help you write queries more easily you could probably add a couple of properties to your ObjectContext that look like this:
public ObjectQuery<BlogEntity> Blogs{
get{
return ctx.Posts.OfType<BlogEntry>() as ObjectQuery<BlogEntry>;
}
}
and the same for comments.
Hope this helps
Alex