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
Related
One of the overload methods of SelectList (from the Microsoft.AspNetCore.Mvc.Rendering namespace) is defined as:
public SelectList(IEnumerable items, string dataValueField, string dataTextField);
When I scaffold an "MVC Controller with view, using Entity Framework" and I create my CRUD pages, I may see the following method inside of my Controller:
public IActionResult Create()
{
ViewData["Continent"] = new SelectList(_context.Continent, **"ContinentID", "ContinentID"**);
ViewData["Country"] = new SelectList(_context.Country, **"CountryID", "CountryName"**);
return View();
}
The field supplied to the dataTextField parameter is different between Continent/Country. How does MVC/EntityFramework decide which field to supply to dataTextField when scaffolding a Controller? Is there something in the individual models or in the DbContext that I am overlooking? I'd like for the dataTextField of Continent to be "ContinentName" so that I don't have to change it manually in the future when I need to delete and then re-scaffold the Controller.
Edit:
Here are the model definitions:
The Model of the Controller that I posted above:
using System;
using System.Collections.Generic;
namespace Project.Models
{
public partial class ProjectForm
{
public int ProjectFormID { get; set; }
public int ContinentID { get; set; }
public int CountryID { get; set; }
public virtual Continent ContinentNavigation { get; set; }
public virtual Country CountryNavigation { get; set; }
}
}
The one that displays the "CountryName" in the dataTextField the way that I want to see it:
namespace Project.Models
{
public partial class Country
{
public int CountryID { get; set; }
public string CountryName { get; set; }
public virtual ICollection<ProjectForm> ProjectForm { get; set; }
}
}
The one that displays the "ContinentID" in the dataTextField the way that I do NOT want to see it:
namespace Project.Models
{
public partial class Continent
{
public int ContinentID { get; set; }
public string ContinentName { get; set; }
public virtual ICollection<ProjectForm> ProjectForm { get; set; }
}
}
There is nothing obviously different to me in the model definitions unfortunately.
I stumbled across this post today (a bit late), but see it still hasn't been answered.
While I can't say why the scaffolding chose to use one field over another in your scenarios (unless you initially had your class/model written differently the last time you cleaned/built your project), I can say how to force it to use a specific column.
Add the DisplayColumn attribute to your class. You will need to rebuild before scaffolding again for the change to take.
namespace Project.Models
{
[DisplayColumn("ContinentName")]
public partial class Continent
{
public int ContinentID { get; set; }
public string ContinentName { get; set; }
public virtual ICollection<ProjectForm> ProjectForm { get; set; }
}
}
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.
The problem is that i cannot understand what is the meaning of the Dbset used in it.
Is it a way to initialize a list or is it a part of Entity Framework??
Have you read Microsofts documentation?
"A DbSet represents the collection of all entities in the context, or that can be queried from the database, of a given type."
An easy way to think about it is that a DbSet represents a table in your database. It is almost always used together with something called a DbContext, which essentially is a representation of a database connection.
Example code that shows how several DbSets are used together with a DbContext:
public class User
{
public string Name { get; set; }
}
public class UserGroup
{
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class ExampleDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
}
Please refer to this tutorial on how to get started with Entity Framework.
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
When I try to create my own POCO classes I get this error. This is only when I got a list of some kind or acsosiation like in this case the Author got Books. But it works great when I use the T4. I kinda like to create my own classes because then I could add my AddBook() to it.. so I highly appreciate if anybody know why..
Schema specified is not valid. Errors:
The relationship 'EworkModel.AuthorBook' was not loaded because the type 'EworkModel.Book' is not available.
The following information may be useful in resolving the previous error:
The required property 'AuthorId' does not exist on the type 'EntityWork.Model.Book'.
my classes look like this
public class Author
{
public virtual int AuthorId { get; set; }
public virtual string Name { get; set; }
public List<Book> Books { get; set; }
}
public class Book
{
public virtual int BookId { get; set; }
public virtual string Title { get; set; }
public virtual Author Author { get; set; }
}
private ObjectSet<Author> _authors;
private ObjectSet<Book> _books;
public EntityWorkContext()
: base("name=EworkEntities", "EworkEntities")
{
_authors = CreateObjectSet<Author>();
_books = CreateObjectSet<Book>();
ContextOptions.LazyLoadingEnabled = true;
}
public ObjectSet<Author> Authors
{
get
{
return _authors;
}
}
public ObjectSet<Book> Books
{
get
{
return _books;
}
}
public void Save()
{
SaveChanges();
}
Seems like EF is looking for the foreign key in your Book entity. Maybe you did not exclude foreign key mapping.
Anyway, if you use the t4 generated POCOs you can still add custom methods like AddBook() by creating a partial class, because the t4 generated classes are partial.