I've been following the contoso university tutorial on Microsoft´s site and I have some doubts about how the Entity Framework is doing some stuff. Here we go...
On the beginning of the tutorial, we created three classes that will be turned into tables in my database.
I´d like to know when and how entity instantiates my classes to populate the objects.
Im using code-first approach.
Example:
The classes im using:
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public decimal? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
After following the tutorial I can now list all my Students and theirs respectives Course(s), as well as their properties in enrollments lists.
When did entity instantiated my Student classes and populated it with the respective Enrollment(s) lists?
How does it know what enrollments are linked to that student?
I dont see any new constructor() ever beeing called.
This might be simple but Im kinda lost here.
Thanks
It doesn't instantiate anything ahead of time, and it doesn't have to. And there won't be anything in the table representing the virtual property as such, either.
These virtual properties are used by EF at runtime to store navigation information, and the collections can be null or contain 0 or more elements. There's no magic, it's just by design that EF "new"s up the collection when it needs to.
If you want to be explicit about it, you can create a constructor on your POCO as well and set your collection equal to a new hashtable.
Related
Not sure how to explain this, but here goes...
I've built a code first data model using EF 4.3. One of classes, "Address" contains typical address data, street, city, state, etc. Other classes in the model contain instances of the "Address" class.
The problem. The data will be gathered/presented using different views, some of which will require the address fields, others that will not.
I can build different view models, each having the necessary validation attributes, and copy the data back and forth between data model and view model but that seems wrong.
What am I missing? There has to be a smarter way to do this.
Thanks for your help,
Jimmy
First read these questions and their answers:
MVC: Data Models and View Models
Why Two Classes, View Model and Domain Model?
also this article could help:
ASP.NET MVC View Model Patterns
In conclusion, I think in most scenarios it's helpful to have a chubby domain model (DM) but light weight presentation models (PM) related to it. So when we want to edit only a small chunk of that fat DM, one of our PMs will raise its hand.
Imagine this class in DM:
namespace DomainModels
{
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime? DoB { get; set; }
public MyAddressDM Address { get; set; }
public string Phone { get; set; }
public IEnumerable<MyCarModel> Cars { get; set; }
//etc.
}
}
Now imagine that in one view we need to edit only Address and Phone. A light weight PM could be like:
namesapce PresentationModels
{
public PersonAddressPhone
{
public int ID { get; set;}
public string FullName { get; set;}
public string AddressSteet { get; set; }
public string AddressCity { get; set; }
public string AddressState { get; set; }
public string AddressZipCode { get; set; }
public string Phone { get; set; }
}
}
and in another view we need to add/remove cars for a person:
namesapce PresentationModels
{
public PersonCars
{
public int ID { get; set;}
public string FullName { get; set;}
public IEnumerable<PMCar> Cars { get; set;}
}
}
Mapping between DO and PM is the golden piece of this puzzle. Be sure to take a look at AutoMapper.
I'm trying to find out how I can re-use a simple 'Comment' entity type for multiple scenarios where something is 'commentable' in my application.
At the moment, I have a couple of entities that a user is able to post comments to. Examples include Blogs, Profiles and Photos - these can all be 'commented' on.
I'd like to be able to use the same 'Comment' class for each of these scenarios, but I don't want to end up with one HUGE table full of comments for everything. I figure it would be much more efficient to at least store a table of BlogComments, PhotoComments, and ProfileComments. At the moment, my Comment class looks like this:
public class Comment
{
[Key]
public int Id { get; set; }
public int ContextId { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
Presumably, I'd need the 'ContextId' field to refer to the particular thing being commented on. This Id might be the Id of a Blog, a Profile or a Photo. I was hoping to be able to refer to comments much like a normal ICollection in these classes, and I have some code like this for the Photos as an example:
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
I've been pointed to various articles during my searches, but none really seem relevant to my particular situation. How can I map these comment collections to different tables, and avoid having a comment "super-table"?
Any help, pointers or advice would be hugely appreciated :)
You can create an abstract Comment class and inherit from it specific comments such as PhotoComment, ProfileComment. You will be able to map the comments to different tables.
public abstract class Comment
{
[Key]
public int Id { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
public class PhotoComment : Comment
{
public int PhotoId { get; set; }
public virtual Photo Photo { get; set; }
}
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<PhotoComment> Comments { get; set; }
}
I'm trying to create some complex form scheme in a .NET MVC 3 project (or so I think). I'll show a wireframe so that you can understand better.
As you can see from the pic above, it's a form to create a new Car Dealership. I can choose from many available car's manufacturers (in the Add a tab), and each selection creates a new tab in the panel underneath it. In each panel there's a html fieldset, and I can add some new cars on it.
In the end, I want to submit all the form, which comprehends the data from the dealership (e.g. name), the car's brands that it has and the cars available for each brand. How can I do it with MVC 3? I don't know how exactly should I instantiate the model Car Dealership and its respective View. What should the cars in the panels be? An array of inputs? And how can I separate the inputs from one tab from the ones of another? All of this, using Razor!
My real-world scenario is a little bit more complicated, but I'll start by this so that I can try to solve the rest by myself. I'm using jQuery/jQuery UI, but I'm opened to other javascript alternatives (like Backbone, etc - I just don't know them yet). Thanks in advance!
UPDATE (1):
These are my models so far:
public class CarDealership
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ManufactorOnDealership> ManufactorsOnDealership { get; set; }
}
public class Manufactor
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ManufactorOnDealership> DealershipsOfManufactor { get; set; }
}
// Junction class: I believe it's needed because the list of Cars belongs to this relationship
public class ManufactorOnDealership
{
public int Id { get; set; }
public int CarDealershipId { get; set; }
public int ManufactorId { get; set; }
public virtual CarDealership CarDealership { get; set; }
public virtual Manufactor Manufactor { get; set; }
public virtual ICollection<Car> CarsOnDealership { get; set; }
}
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ManufactorOnDealership> Manufactors { get; set; }
}
So I decided to go with the code first/DbContext approach, but already have an existing database file. Nothing complex, so I am thinking I can just create the DbContext derived container class with DbSets for the respective POCO's, create the connection string to my database and I should be set. However I believe I am having difficulties properly declaring the properties in my entity classes since I am getting errors when trying access an object through the navigational properties. Usually telling me Object reference not set to an instance of an object when I try context.Products.Find(1).Category.CATNAME; etc. Also tried declaring the collection properties with virtual keyword to no avail.
Some specifics of the database schema are:
In Categories table the PCATID is a foreign key to the CategoryID in
the same Categories table and can be null.
Both CategoryID and RootCategoryID in Products table can be null and
are both foreign keys to CategoryID in the Categories table.
I am testing things at the moment but will be setting a lot of the fields to non null types eventually.
Here are my entity POCO's and the entity Dbset container class:
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CATNAME { get; set; }
public int PCATID { get; set; }
public ICollection<Category> Categories { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
[Key]
public int ProductID { get; set; }
public int CategoryID { get; set; }
public int RootCategoryID { get; set; }
public string Name { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string Keywords { get; set; }
public decimal ListPrice { get; set; }
public Category Category { get; set; }
}
public class EFDbContext: DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
You need to make PCATID a nullable property as you have said it can be null. Make all those navigation properties and collection properties virtual. EF will not be able to detect the category hierarchy so you have use either attributes or fluent API to configure that.
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CATNAME { get; set; }
[ForeignKey("ParentCategory")]
public int? PCATID { get; set; }
[InverseProperty("Categories")]
public virtual Category ParentCategory { get; set; }
[InverseProperty("ParentCategory")]
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Requirements for Creating POCO Proxies
Everything looks ready for POCO but Lazy Loading isn't sorted out at this point. By default LL is on, but in order to enable lazy loading, the Category property must be Virtual (a proxy is created that catches the reference and loads the data). If you don't want lazy loading then disable it in your EFDbContext constructor.
So your options are:
public virtual Category Category { get; set; }
or
public class EFDbContext: DbContext
{
public static EFDbContext()
{
LazyLoadingEnabled = false
}
...
}
You'd probably want to do the first one...
Are you certain you really want to use Code First? Or do you just want to use DbContext and DbSet? You can get the same benefits with Database First, using DbContext and DbSet. Since you already have a database, it's generally a lot simpler.
See: http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-model-amp-database-first-walkthrough.aspx
The only difference between Code First and Database First with DbContext is that Code first uses the fluent mapping model, while Database First uses an .edmx file. Maintaining the .edmx is much easier with an existing database.
If you're bound and determined to use Code First, then I suggest getting the Entity Framework Power Tools CTP1 and reverse engineering your database to Code First.
I agree with #Eranga about class Category (+1 to #Eranga).
public class Category {
[Key]
public int CategoryID { get; set; }
public string CATNAME { get; set; }
[ForeignKey("ParentCategory")]
public int? PCATID { get; set; }
[InverseProperty("Categories")]
public virtual Category ParentCategory { get; set; }
[InverseProperty("ParentCategory")]
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
And you also have problem with your Linq query:
context.Products.Find(1).Category.CATNAME;
EF return data only from tables that you request with Include or you use this tables in functions.
With this code all work:
db.Products
.Include(p => p.Category) // here I demand to load data from Category table
.First(p => p.ProductID == 3)
.Category
.CATNAME;
This is a question about Models, ASP.NET MVC 3 and relationships. I'm using the code-first approach.
Imagine this simple typical scenario of an User with its Blog Posts:
public class User
{
public virtual int UserId { get; set; }
public virtual string Nickname { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public virtual int PostId { get; set; }
public virtual string BlogText { get; set; }
}
I made it very very simple.
Well. When I create a controller using the Controller with Read/Write actions and views using Entity Framework, the collection of Posts is not considered. It's being ignored. So, I can't get this relationship to work.
If, in place of the collection, there was a single object (public virtual string Email, for example) it works normally.
I'm asking myself and to you:
Why
How do I put references to collections in my model?
It should be a simple task, I really can't understand why it doesn't work.
Thank you.
POCO should look like below to have one-to-many relationship:
public class Post
{
public int Id { get; set; }
public string BlogText { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int Id { get; set; }
public string Nickname { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}