Platfrom : MVC4
Hi,
I am new to MVC4 and I am trying to retrieve the data from SQL using Entity framework, but it fails. Here is the code I have tried so far :
Model: MovieListing.cs
[Table("MovieMaster")]
public class MoviesListing
{
public int MovieId { get; set; }
public string Name { get; set; }
public string year { get; set; }
public string Image { get; set; }
public string producer { get; set; }
public string plot { get; set; }
}
Model:
public class MovieContext:DbContext
{
public DbSet<MoviesListing> Movies { get; set; }
}
View :
#model MovieManiac.Models.MoviesListing
#{
ViewBag.Title = "Moviedetails";
}
<h2>Moviedetails</h2>
<table>
<tr>
<td>Movie Name</td>
<td> #Model.Moviename</td>
</tr>
</table>
Controller:
public ActionResult MovieDetails(int id)
{
MovieContext objMovies = new MovieContext();
MoviesListing movie = objMovies.Movies.Single(mov => mov.MovieId == id);
return View(movie);
}
So, in the browser, when I enter the Id, I want to retrieve information from database.I am using local sql db. Any suggestions?
Connection String:
</configSections>
<connectionStrings>
<add name="MovieContext" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MovieManiac-20150318225934;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MovieManiac-20150318225934.mdf" />
</connectionStrings>
error:
An exception of type 'System.Data.Entity.ModelConfiguration.ModelValidationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'MoviesListing' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'Movies' is based on type 'MoviesListing' that has no keys defined.
You need to add the Key attribute to the I'd property so EF knows what is the primary key
https://msdn.microsoft.com/en-us/data/jj591583.aspx
http://www.entityframeworktutorial.net/code-first/key-dataannotations-attribute-in-code-first.aspx
Update
You may be able to use int and numeric, although I wouldn't bother myself, just change your model to decimal to match the datatype mapping... https://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx... if you have a need to use numeric.
Also decorate your class with the Key attribute
[Table("MovieMaster")]
public class MoviesListing
{
[Key]
public int MovieId { get; set; }
public string Name { get; set; }
public string year { get; set; }
public string Image { get; set; }
public string producer { get; set; }
public string plot { get; set; }
}
You can read about using a decimal (numeric) as a key http://dotnetwindow.blogspot.co.uk/2012/06/using-decimal-as-primary-key-in-entity.html
BTW, are you using code first?
add property to your entity
[Table("MovieMaster")]
public class MoviesListing
{
public int Id { get; set; } // add this
public int MovieId { get; set; }
public string Name { get; set; }
public string year { get; set; }
public string Image { get; set; }
public string producer { get; set; }
public string plot { get; set; }
}
or else use this
public class MovieContext:DbContext
{
public DbSet<MoviesListing> Movies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MoviesListing>.HasKey(x => x.MovieId );
base.OnModelCreating(modelBuilder);
}
}
Related
I'm rewriting this question:
I have 2 models. Entry and Topic.
public class Entry
{
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
public virtual ICollection<Topic> TopicList { get; set; }
}
public class Topic
{
public int TopicId { get; set; }
public String TopicName { get; set; }
}
I followed an example on ASP.Net/MVC to set up my models this way.
What I would like to do is for every entry item I have a TopicId, but then I'd like to convert that to a TopicName by accessing my TopicList.
My question is, how do I load TopicList?
In the examples I'm following I'm seeing something about LazyLoading and EagerLoading, but it doesn't seem to be working.
I tried doing the following from my Entry controller:
db.Entries.Include(x => x.TopicList).Load();
But that still gives me a TopicList of 0 (which is better than null)
How can I do this?
In my view I'm binding to the Entries like this:
#model IEnumerable<projectInterview.Models.Entry>
I would like to access the TopicList here:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.TopicId)
</td>
...
</tr>
I'd like to use the TopicId in this loop and display the TopicName that is part of the object in the collection.
I'm assuming you're following an Entity Framework example. You're trying to create a one-to-many relationship, as far as I can tell, although I'm unsure about which end is which.
In the general case, to establish a one-to-many relationship, you have to do something like this:
public class One
{
[Key]
public int Id { get; set; }
public virtual ICollection<Many> Many { get; set; }
}
public class Many
{
[Key]
public int Id { get; set; }
[ForeignKey("One")]
public int OneId { get; set; }
public virtual One One { get; set; }
}
If what you're trying to do is have one Entry relating to many Topic objects, then you're almost there but you're lacking something.
For the ICollection<Topic> to actually contain anything, the (many) Topic objects need to have a foreign key to the (one) Entry. (It also doesn't hurt to explicitly mark the primary key on both sides, rather than relying on the EF conventions.)
public class Topic
{
[Key]
public int TopicId { get; set; }
public String TopicName { get; set; }
[ForeignKey("Entry")]
public int EntryId { get; set; }
public virtual Entry Entry { get; set; }
}
public class Entry
{
[Key]
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
public virtual ICollection<Topic> TopicList { get; set; }
}
Now TopicList should be an actual and populated collection, without the need to do an Include.
If, on the other hand, you want one Topic relating to many Entry objects, then you have it a little backwards. The correct way would be:
public class Topic
{
[Key]
public int TopicId { get; set; }
public String TopicName { get; set; }
public virtual ICollection <Entry> Entries { get; set; }
}
public class Entry
{
[Key]
public int EntryId { get; set; }
public int UserId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
[ForeignKey("Topic")]
public int TopicId { get; set; }
public virtual Topic Topic { get; set; }
}
In this case, you may or may not use db.Entries.Include(x => x.Topic) depending on whether you want them loaded all at once or one-by-one on demand. Regardless of what you choose, the following expression should return the proper value:
myEntry.Topic.TopicName
If I understand you correctly you have added the list of Topics to the Entry just to get the name of the topic when displaying the entry. The best way to do this is to actually have a Topic property in your entry model. So your model would look like this:
public class Entry
{
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
//Change this.....
public virtual Topic Topic { get; set; }
}
Then in your view you would use (assuming the Model is an IEnumerable):
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => modelItem.Topic.TopicName )
</td>
...
</tr>
This link has a great example of how to do this:
http://weblogs.asp.net/manavi/archive/2011/03/28/associations-in-ef-4-1-code-first-part-2-complex-types.aspx
In my opinion problem is with casting. In view you have IEnumerable<projectInterview.Models.Entry> while Topics is ICollection<Topic>, which is a collection of different type
Topics = null means there are no Topics in the list to iterate over. How do you fill them? Your view expects IEnumerable how do you cast your topics to the entries?
Based on the original question I've added a small working example, maybe it helps you to find your bug.
Controller:
public class TestController : Controller
{
public ActionResult Index()
{
var viewModel = new ViewModel()
{
Topics = new List<Topic>()
};
viewModel.Topics.Add(new Topic() { header = "test" });
viewModel.Topics.Add(new Topic() { header = "test2" });
return View(viewModel);
}
}
Model:
public class ViewModel
{
public virtual ICollection<Topic> Topics { get; set; }
public int getCount()
{
return Topics.Count;
}
}
public class Topic
{
public string header { get; set; }
}
View:
#model testProject.Models.ViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Model.getCount()
#foreach(var item in Model.Topics)
{
<div>#item.header</div>
}
Output:
Index
2
test
test2
It seems that you are not initializing your Topics anywhere in the code. If the collection is null it means it is not initialized. If you instantiate it with
ICollection<Topic> Topics = new List<Topic>();
Once initialized you should receive zero when calling Topics.Count. If you do not make a call to a database it will stay zero.
In your case check whether you are instantiating the Topics.
Having problem in displaying relational properties b/w two tables having one(company) to many(package_master) relationship
Action
public ViewResult Index()
{
var companies = db.companies.Include(c => c.aspnet_Users)
.Include(c=>c.package_master);
return View(companies.ToList());
}
EntitySet
public partial class company
{
public company()
{
this.package_master = new HashSet<package_master>();
}
public int company_id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
public string fax { get; set; }
public Nullable<System.Guid> sen_sup { get; set; }
public virtual aspnet_Users aspnet_Users { get; set; }
public virtual ICollection<package_master> package_master { get; set; }
}
When I type Model.aspnet_Users.property1 everything works fine(intellisense) but now I also want to diaplay properties from packege_master(no intellisense)(foreign key table=package_master having client_id as foreign key, public key table=company having company_id as primary key)
package_master is a collection. You cannot access member properties of package_master entities directly like: Model.package_master.XXX. You must iterate the collection to get access to entities.
When running my first asp.net mvc application I got this error
I thought that entity framework automatically would create the keys of column names that end with Id? isnt it correct?
As you can see the ApplicantPositionID would be a table with 2 columns as primary key because it would relate to Applicants and also to Position.
One or more validation errors were detected during model generation:
System.Data.Edm.EdmEntityType: : EntityType 'ApplicantImage' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntityType: : EntityType 'ApplicationPositionHistory' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ApplicantsPositions� is based on type �ApplicantPosition� that has no keys defined.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ApplicantImages� is based on type �ApplicantImage� that has no keys defined.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ApplicationsPositionHistory� is based on type �ApplicationPositionHistory� that has no keys defined.
The error is thrown in this line:
public ActionResult Index()
{
return View(db.Positions.ToList());
}
And my model is the following one:
namespace HRRazorForms.Models
{
public class Position
{
public int PositionID { get; set; }
[StringLength(20, MinimumLength=3)]
public string name { get; set; }
public int yearsExperienceRequired { get; set; }
public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
[StringLength(20, MinimumLength = 3)]
public string name { get; set; }
public string telephone { get; set; }
public string skypeuser { get; set; }
public ApplicantImage photo { get; set; }
public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
}
public class ApplicantPosition
{
public int ApplicantID { get; set; }
public int PositionID { get; set; }
public virtual Position Position { get; set; }
public virtual Applicant Applicant { get; set; }
public DateTime appliedDate { get; set; }
public int StatusValue { get; set; }
public Status Status
{
get { return (Status)StatusValue; }
set { StatusValue = (int)value; }
}
//[NotMapped]
//public int numberOfApplicantsApplied
//{
// get
// {
// int query =
// (from ap in Position
// where ap.Status == (int)Status.Applied
// select ap
// ).Count();
// return query;
// }
//}
}
public class ApplicantImage
{
public int ApplicantId { get; private set; }
public byte[] Image { get; set; }
}
public class Address
{
[StringLength(20, MinimumLength = 3)]
public string Country { get; set; }
[StringLength(20, MinimumLength = 3)]
public string City { get; set; }
[StringLength(20, MinimumLength = 3)]
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
public class ApplicationPositionHistory
{
public ApplicantPosition applicantPosition { get; set; }
public Status oldStatus { get; set; }
public Status newStatus { get; set; }
[StringLength(500, MinimumLength = 10)]
public string comments { get; set; }
public DateTime dateModified { get; set; }
}
public enum Status
{
Applied,
AcceptedByHR,
AcceptedByTechnicalDepartment,
InterviewedByHR,
InterviewedByTechnicalDepartment,
InterviewedByGeneralManager,
AcceptedByGeneralManager,
NotAccepted
}
}
EF Code First can only infer that a property is a primary key if the property is called Id or <class name>Id (or if it is annotated with the Key attribute).
So you need to extend your e.g. ApplicantImage with an ApplicantImageId or Id property etc.
Edit: An artice about the coneventions: Conventions for Code First
You can add the [Key] atributte to the property ApplicantId or do it via Fluent API overriding OnModelCreating method DbContext
modelBuilder.Entity<ApplicantImage >().HasKey(p => p.ApplicantId);
In your case, EF naming convention first looks for an ID (case-insensitive) column. If nothing, looks for ApplicantImageId and when it founds nothing, it raises that error.
So, you should add the [Key] attribute on your ID:
public class ApplicantImage
{
[Key]
public int ApplicantId { get; private set; }
public byte[] Image { get; set; }
}
and if ApplicantId column is identity in your database, you should add another attribute too:
public class ApplicantImage
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ApplicantId { get; private set; }
public byte[] Image { get; set; }
}
I know this is an old question but it is still relevant. I ran into the same situation however we use a .tt file to generate the .cs from our edmx. Our .tt is setup to add the [Key] attribute on our first column of the table for most situations, but in my case i was using a row over () in SQL to generate unique id's for the first column (works great for most situations). The problem with that was it makes a nullable and the .tt wasn't setup to add [Key] in this case.
Wrapping the row Over() in a ISNULL ((),0) was able to fix making the column not null and solved my problem. Otherwise, as mentioned by marianosz, simply using the .HasKey() in your data context will work fine too.
I have two POCOs in my "Bookshelf" test application:
/// <summary>
/// Represents a book
/// </summary>
public class Book
{
public int ID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string ISBN { get; set; }
public virtual Loaner LoanedTo { get; set; }
}
/// <summary>
/// Represents a Loaner
/// </summary>
public class Loaner
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Loans { get; set; }
}
Is there a way that my LoanedTo could be nullable? I mean a book isn't always loaned, right! I tried
public virtual Loaner? LoanedTo { get; set; }
But I get:
The type 'RebtelTests.Models.Loaner' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
So I must be thinking wrong somewhere, but I can't figure it out. Probably easy squeeze for you guys.
You don't need to do anything special. Classes are always nullable.
I just tried this (with MVC3):
In my Models directory:
namespace MvcApplication2.Models
{
public class Book
{
public int ID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string ISBN { get; set; }
public virtual Loaner LoanedTo { get; set; }
}
public class Loaner
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Loans { get; set; }
}
public class BookContext : System.Data.Entity.DbContext
{
public System.Data.Entity.DbSet<Book> Books { get; set; }
public System.Data.Entity.DbSet<Loaner> Loaners { get; set; }
}
}
In my HomeController:
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
string message = "OK";
try
{
var context = new Models.BookContext();
var book = new Models.Book();
book.Title = "New Title";
book.Author = "New Author";
book.ISBN = "New ISBN";
context.Books.Add(book);
context.SaveChanges();
}
catch (Exception err)
{
message = err.ToString();
}
ViewBag.Message = message;
return View();
}
}
}
The connectionstring in Web.Config:
<add name="BookContext" connectionString="Data Source=|DataDirectory|BookContext.sdf" providerName="System.Data.SqlServerCe.4.0" />
When I run the application, the view displays "OK". This means that no exception was thrown. When I look in my App_Data folder, a BookContext.sdf file has been created. That database contains a table for the Books and the Loaners. The table for the Loaners is empty. The one for the Books contains one record:
ID: 1; Title: "New Title"; Author: "New Author"; ISBN: "New ISBN"; LoanerID: null
If you are talking about a simple property like int, bool, or float use int?, bool?, or float?
like
public int? ID { get; set; }
public bool? Exists { get; set; }
Couldn't you just use something like this
public virtual Nullable<Loaner> LoanedTo { get; set; }
That then should make LoanedTo a nullable property
I am using EF4 CTP5. Here are my POCOs:
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
Is there a way to get Address to be a ComplexType for the Order class? After playing around with this, I'm guessing not, but maybe there's a way I haven't seen.
EDIT: In response to Shawn below, I gave it my best shot:
//modelBuilder.Entity<Order>().Ignore(o => o.BillingAddress);
//modelBuilder.Entity<Order>().Ignore(o => o.ShippingAddress);
modelBuilder.Entity<Order>()
.Property(o => o.BillingAddress.City).HasColumnName("BillingCity");
Fails at runtime with error "The configured property 'BillingAddress' is not a declared property on the entity 'Order'." Trying to use Ignore() doesn't work. Next, the Hanselman article is CTP4, but the CTP5 equivalent is:
modelBuilder.Entity<Order>().Map(mapconfig =>
{
mapconfig.Properties(o => new {
o.Id
, o.Total
, o.BillingAddress.City
});
mapconfig.ToTable("Orders");
});
Fails with error "Property 'BillingAddress.City' of type 'Order' cannot be included in its mapping."
I give up. Maybe the final release will have something like this. Or maybe I need to switch to NHibernate =)
All you need to do is to place ComplexTypeAttribute on Address class:
[ComplexType]
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
Alternatively, you can achieve this by fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Address>();
}
But you cannot have Address type as to be both an Entity and a Complex Type, it's one way or another.
Take a look at this blog post where I discuss this at length:
Associations in EF Code First CTP5: Part 1 – Complex Types
If you want Address to be in the same table as Order, you're going to have to tell EF that in the DbContext OnModelCreating override.
Take a look here: http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx