I'm developing the Music Store sample app with ASP.NET MVC, Entity Framework and WCF.
This is a layered application which has a common layer for the entities.
in the AddToCart Action method , the Album object is populates fine but after the Cart save when wcf loads the cart object the associated Album object is Null, may be some serialization issue (I have not idea), in the view #foreach (var item in Model.CartItems) the item.Album.Title becomes null
This is my code:
public static void AddToCart(Album album, string ShoppingCartID)
{
using (MusicStoreEntities db = new MusicStoreEntities())
{
// Get the matching cart and album instances
var cartItem = db.Carts.SingleOrDefault(
c => c.CartId == ShoppingCartID
&& c.AlbumId == album.AlbumId);
if (cartItem == null)
{
// Create a new cart item if no cart item exists
cartItem = new Cart
{
AlbumId = album.AlbumId,
CartId = ShoppingCartID,
Count = 1,
DateCreated = DateTime.Now
};
db.Carts.Add(cartItem);
}
else
{
// If the item does exist in the cart, then add one to the quantity
cartItem.Count++;
}
// Save changes
db.SaveChanges();
}
}
public static List<Cart> GetCartItems(string ShoppingCartID)
{
using (MusicStoreEntities db = new MusicStoreEntities())
{
return db.Carts.Where(cart => cart.CartId == ShoppingCartID).ToList();
}
}
Controller
namespace MusicStore.Web.Controllers
{
public class ShoppingCartController : Controller
{
MusicShoppingCartMgr.Cart serviceref1 = new MusicShoppingCartMgr.Cart();
MusicShoppingCartMgr.iShoppingCart servicemethodref1 = new iShoppingCartClient();
//
// GET: /ShoppingCart/
public ActionResult Index()
{
var cart = ShoppingCart.GetCart(this.HttpContext);
// Set up our ViewModel
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(cart.ShoppingCartId),
CartTotal = cart.GetTotal(cart.ShoppingCartId)
};
// Return the view
return View(viewModel);
}
//
// GET: /Store/AddToCart/5
public ActionResult AddToCart(int id)
{
var addedAlbum = servicemethodref1.GetAlbum(id);
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCart(addedAlbum, cart.ShoppingCartId);
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
}
}
Model Classes
namespace MusicStore.Core
{
[Serializable]
[DataContract]
public class Cart
{
[Key]
[DataMember]
public int RecordId { get; set; }
[DataMember]
public string CartId { get; set; }
[DataMember]
public int AlbumId { get; set; }
[DataMember]
public int Count { get; set; }
[DataMember]
public System.DateTime DateCreated { get; set; }
[DataMember]
public virtual Album Album { get; set; }
}
}
namespace MusicStore.Core
{
[Serializable]
[DataContract]
//[Bind(Exclude = "AlbumId")]
public class Album
{
[DataMember]
[ScaffoldColumn(false)]
public int AlbumId { get; set; }
[DataMember]
[DisplayName("Genre")]
public int GenreId { get; set; }
[DataMember]
[DisplayName("Artist")]
public int ArtistId { get; set; }
[DataMember]
[Required(ErrorMessage = "An Album Title is required")]
[StringLength(160)]
public string Title { get; set; }
[DataMember]
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00,
ErrorMessage = "Price must be between 0.01 and 100.00")]
public decimal Price { get; set; }
[DataMember]
[DisplayName("Album Art URL")]
[StringLength(1024)]
public string AlbumArtUrl { get; set; }
[DataMember]
public virtual Genre Genre { get; set; }
[DataMember]
public virtual Artist Artist { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
}
DB Context
namespace MusicStore.Data
{
public class MusicStoreEntities : DbContext
{
public MusicStoreEntities()
: base("MusicStoreEntities")
{
var ensureDLLIsCopied = System.Data.Entity.SqlServer.SqlProviderServices.Instance;
this.Configuration.ProxyCreationEnabled = false;
Database.SetInitializer<MusicStoreEntities>(new CreateDatabaseIfNotExists<MusicStoreEntities>());
Database.SetInitializer(new CommonDBInitializer());
}
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<Cart> Carts { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderDetail> OrderDetails { get; set; }
}
}
Question is: why it is the Album object is not loaded inside Cart object, and how to fix it?
Entity Framework does not load related objects by default to help prevent potential performance problems around loading one-to-many or many-to-many relationships into memory.
Looking at the code you have posted, it seems a potential fix would be for you to add .Include("Album") when getting out the cart items in GetCartItems. It would then become
public static List<Cart> GetCartItems(string ShoppingCartID)
{
using (MusicStoreEntities db = new MusicStoreEntities())
{
return db.Carts.Include("Album").Where(cart => cart.CartId == ShoppingCartID).ToList();
}
}
See the Entity Framework docs for some other options around loading related entities
Related
I use a many-to-many relationship. When I want to add an item to the cart I get this exception:
An error occurred while updating the entities. Invalid object name 'CartClothesItem'
I don't even have the entity named 'CartClothesItem'.
This is my controller code:
[HttpPost("Add")]
public async Task<ActionResult> AddItemIntoCart(int userId, int itemId)
{
if (userId <= 0 || itemId <= 0)
return BadRequest("The parameters are invalid!");
var userCart = _context.Carts.SingleOrDefault(c => c.UserId == userId);
var item = _context.ClothesItems.SingleOrDefault(i => i.ClothesItemId == itemId);
if (item == null)
return NotFound("The item doesn't exist");
if (userCart == null)
return NotFound("The user doesn't exist");
if (userCart.Items.Where(i => i.ClothesItemId == itemId).Any())
return BadRequest("You already have this item in the cart");
userCart.Items.Add(item);
await _context.SaveChangesAsync();
return Ok("The item was successfully added into your cart!");
}
Item model class:
public class СlothesItem
{
[Key]
public int ClothesItemId { get; set; }
[Required]
public string ModelName { get; set; }
[Required]
public string Brand { get; set; }
[Required]
public string Size { get; set; }
public string Description { get; set; }
[Range(0.0, Double.MaxValue, ErrorMessage = "The price can't be negative.")]
public double Price { get; set; }
[Required]
[ClothesItem_EnsureThisClothesTypeExists]
public string Type { get; set; }
public virtual ICollection<Photo> Photos { get; set; }
[JsonIgnore]
public virtual ICollection<Cart> Carts { get; set; }
public СlothesItem()
{
Photos = new List<Photo>();
Carts = new List<Cart>();
}
}
Cart model class:
public class Cart
{
[Key]
public int CartId { get; set; }
public int UserId { get; set; }
[ForeignKey(nameof(UserId))]
public User User { get; set; }
public virtual ICollection<СlothesItem> Items { get; set; }
public Cart()
{
Items = new List<СlothesItem>();
}
}
I have 2 tables in my entity framework:
INATIVOS (Employees)
EMPRESAS (Companies)
When registering an employee I select a company in a #Html.DropDownListFor (List).
The registration is ok, the company is saved correctly. However, when trying to edit a registered employee shows the error "Unable to set field/property on entity" in the Companies list.
INATIVO.cs
public partial class INATIVOS
{
public decimal ID { get; set; }
public string COD_EMPRESA { get; set; }
public string CHAPA { get; set; }
public string NOME { get; set; }
public System.DateTime DATA_NASC { get; set; }
public string PLANO { get; set; }
public short LEI { get; set; }
public short APOSENTADO { get; set; }
public short ESTADO_VIDA { get; set; }
public short ISENTO { get; set; }
public Nullable<System.DateTime> INICIO_VIGENCIA { get; set; }
public Nullable<System.DateTime> FIM_VIGENCIA { get; set; }
public string CPF { get; set; }
public string EMAIL { get; set; }
public string ENDERECO { get; set; }
public string NUMERO { get; set; }
public string COMPLEMENTO { get; set; }
public string BAIRRO { get; set; }
public string CIDADE { get; set; }
public string ESTADO { get; set; }
public string CEP { get; set; }
public string TELEFONE { get; set; }
public string CELULAR { get; set; }
public string OBSERVACAO { get; set; }
public List<DEPENDENTES> DEPENDENTES { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
}
InativoController.cs
public ActionResult Index(int? id)
{
INATIVOS inaModel = new INATIVOS();
using (Entidades db = new Entidades())
{
if (id != null)
{
inaModel = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
inaModel.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
inaModel.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(inaModel);
}
If these are navigation properties:
public List<DEPENDENTES> DEPENDENTES { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
Then (1) they need to be virtual and (2) they need to be something like IList or ICollection:
public virtual ICollection<DEPENDENTES> DEPENDENTES { get; set; }
public virtual ICollection<EMPRESAS> EMPRESAS { get; set; }
public virtual ICollection<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
Though, as an aside, what you're doing here is very strange:
inaModel.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
inaModel.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
Essentially what you have in the database is, for a given Employee (INATIVOS) there are relationships to specific Companies (EMPRESAS) and specific Medical Plans (PLANOS_MEDICO). But you're ignoring whatever is in that data and replacing it with all companies and all medical plans in the entire database.
So every time you use this controller action to fetch an existing employee record, it's going to look like that employee has every company and every medical plan. Even though that's not what's in the database. I strongly suspect that's not what you want.
UPDATE: Based on comments on this answer, it sounds like those aren't navigation properties. They're not even properties of the model at all. They're just lists of data needed for the view to populate (presumably) <select> elements.
If they're not part of the data model then remove them from the model. Instead, consider using a view model. For example:
public class InativosViewModel
{
public INATIVOS Inativos { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
}
Then in the controller return an instance of the view model, which is a composite object of the model and the data needed for the view:
public ActionResult Index(int? id)
{
InativosViewModel result = new InativosViewModel();
using (Entidades db = new Entidades())
{
if (id != null)
{
result.Inativos = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
result.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
result.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(result);
}
And of course change the model binding in the view itself to now expect and use an instance of InativosViewModel. The resulting POST action can still accept an instance of INATIVOS if it needs to, or it can accept an instance of InativosViewModel just as well. That all depends on what the form structure is and what's being posted to that action.
Alternatively, if you want to keep using the INATIVOS model then still remove those lists from it but use something like ViewBag to send them to the view. Something like this:
public ActionResult Index(int? id)
{
INATIVOS inaModel = new INATIVOS();
using (Entidades db = new Entidades())
{
if (id != null)
{
inaModel = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
ViewBag.Empresas = db.EMPRESAS.ToList<EMPRESAS>();
ViewBag.PlanosMedico = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(inaModel);
}
Then in your view you would populate the <select> elements from there:
#Html.DropDownListFor(
model => Model.COD_EMPRESA,
new SelectList(ViewBag.Empresas, "CODIGO", "DESCRICAO"),
htmlAttributes: new { #class = "form-control"
})
There is a Products table. Each product has a list of reviews. When products are added, initially they don't have reviews. I am trying to add a review to an existing product by adding to the List<Review> inside the product but it's throwing an error
Multiplicity constraint violated
Product.cs
namespace DatabaseProject.Models
{
public class Product
{
public Product()
{
Reviews = new List < Review >();
}
public int Id { get; set; }
public Catagory Catagory { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Specification { get; set; }
public List<Review> Reviews { get; set; }
}
}
Review.cs
namespace DatabaseProject.Models
{
public class Review
{
public int Id { get; set; }
public string Text { get; set; }
public int Stars { get; set; }
[Required]
public Product Product { get; set; }
[Required]
public Customer Customer { get; set; }
}
}
Method to add a new product
public Product Add(Product product)
{
using (var context = new ShopDbContext())
{
context.Database.Log = Console.WriteLine;
var response = context.Products.Add(product);
context.SaveChanges();
return response;
}
}
Method to add a new review
public bool AddReview(int id, Review review)
{
using (var context = new ShopDbContext())
{
Product oldProduct = context.Products.Find(id);
if (oldProduct == null)
{
return false;
}
oldProduct.Reviews.Add(review);
context.SaveChanges();
return true;
}
}
Adding a new product. This works fine.
Product p = new Product
{
Catagory = Catagory.COMPUTER,
Name = "Surface Pro 3",
Description = "Tablet / Laptop",
Specification = "i5 16 GB ram",
};
productService.Add(p);
Adding a new review:
Review review = new Review
{
Customer = customerService.Get(1),
Product = productService.Get(1),
Stars = 2,
Text = "It's a good camera",
};
productService.AddReview(1, review);
Throws this error
System.InvalidOperationException: 'Multiplicity constraint violated. The role 'Review_Product_Target' of the relationship 'DatabaseProject.Review_Product' has multiplicity 1 or 0..1.'
EDIT
Sorry I forgot to mention this.
I also have a Customer table which stores a set of reviews made by that customer. Is this somehow causing the error?
namespace DatabaseProject.Models
{
public class Customer
{
public Customer()
{
Addresses = new List<Address>();
Reviews = new List<Review>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public List<Address> Addresses { get; set; }
public List<Review> Reviews { get; set; }
}
}
I've got an edit page that is being populated from a ViewModel. This ViewModel takes in items from a couple of models (Participant, Gender, Country):
ViewModel
namespace MVCManageParticipants.Models
{
public class ParticipantDetailsViewModel
{
public int Id { get; set; }
public int SiteId { get; set; }
public int Status { get; set; }
public string Gender { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string City { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
public string Postcode { get; set; }
public string Telephone { get; set; }
public string Notes { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
}
The IEnumerable Countries beings back a full list of countries and fills a dropdown list with the data from a database (this is something I'm wanting to extend on with City, Gender, Status but need to get one working first).
Side question: is how I am doing this the accepted way to fill a dropdown list on the View from a database?
The page populates fine on the [HttpGet] and sends back the ViewModel to the view.
[HttpGet] Controller
var model = (from p in _db.Participants
where p.Id == id
select new ParticipantDetailsViewModel
{
Id = p.Id,
SiteId = p.SiteId,
Status = p.Status,
Gender = p.Gender.Name,
Title = p.Title,
Name = p.Name,
City = p.City.Name,
Country = p.Country.PrettyName,
CountryId = p.Country.Id,
Postcode = p.Postcode,
Telephone = p.Telephone,
Notes = p.Notes,
Countries = _db.Countrys.ToList()
}).FirstOrDefault();
[HttpPost] controller
public ActionResult Edit(ParticipantDetailsViewModel viewModel)
{
if (ModelState.IsValid)
{
_db.Entry(viewModel).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index", new { id = viewModel.Id });
}
return View(viewModel);
}
Which is giving me an error on the line _db.Entry(viewModel).State = EntityState.Modified;:
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: The entity type ParticipantDetailsViewModel is not part of the model for the current context.
Participant Model
public class Participant
{
public int Id { get; set; }
public int SiteId { get; set; }
public int Status { get; set; }
public Gender Gender { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public City City { get; set; }
public Country Country { get; set; }
public string Postcode { get; set; }
public string Telephone { get; set; }
public string Notes { get; set; }
}
I this because I am trying to update the ViewModel rather than the Participant model? Should I be creating a new Participant object and updating with the incoming data that way?
ParticipantDetailsViewModel is not part of the dbcontext you need to get the Participant object from the database with the id and update it with informations from the viewModel :
public ActionResult Edit(ParticipantDetailsViewModel viewModel)
{
if (ModelState.IsValid)
{
var participant = _db.Participants.FirstOrDefault(p => p.Id == viewModel.Id);
//set all properties whith new values
participant.SiteId = viewModel.SiteId
_db.Entry(participant).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index", new { id = viewModel.Id });
}
return View(viewModel);
}
I am building a Code-First, Many-To-Many relationship between my ApplicationUser class and a Lesson class. When the model is created, Entity Framework builds the two tables and the intersecting pivot table. However, neither table seems to take in data from the pivot table (LessonApplicationUsers). Both List variables do not seem to hold either the list of Students or the list of Lessons. Both entities i'm trying to marry up already exist in the database
ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string Address { get; set; }
public int? Age { get; set; }
public ClassLevel? ClassLevel { get; set; }
public string FirstName { get; set; }
public int? Height { get; set; }
public string LastName { get; set; }
public string MobileNumber { get; set; }
public string Postcode { get; set; }
public string Town { get; set; }
public int? Weight { get; set; }
public ApplicationUser()
{
Lessons = new List<Lesson>();
}
public ICollection<Lesson> Lessons { get; set; }
}
Lesson Class
public class Lesson
{
[Key]
public int LessonID { get; set; }
public LessonType ClassType { get; set; }
public ClassLevel? ClassLevel { get; set; }
public DateTime ClassStartDate { get; set; }
public DateTime ClassEndDate { get; set; }
public float ClassCost { get; set; }
public int? InstructorID { get; set; }
public Lesson()
{
Students = new List<ApplicationUser>();
}
public ICollection<ApplicationUser> Students { get; set; }
public enum LessonType {Group,Private}
}
My DBContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Lesson> Lessons { get; set; }
public DbSet<ApplyViewModel> Applications { get; set; }
And finally, the code i'm using to add in the pivot table data. This is activated when the user presses a button on the booking form.
public ActionResult BookUser()
{
//Gather required variables
ApplicationUser user = db.Users.First(i => i.UserName == User.Identity.Name);
int classID = int.Parse(Request.Form["classID"]);
using (db)
{
var editedLesson = db.Lessons.Single(s => s.LessonID == classID);
db.Lessons.Attach(editedLesson);
var editedUser = db.Users.Single(s => s.Id == user.Id);
db.Users.Attach(editedUser);
editedLesson.Students.Add(editedUser);
db.SaveChanges();
}
return View("Index");
When I try and run it, when i press my book button, it runs through the code and executes. checking the database it has indeed inserted the key values into the pivot table. When i load the model of the lesson to view its details, the Student attribute has a count of 0. I've been at this for days and i've got the feeling i'm missing something kickself simple....but i've gone over it a dozen times and can't see what i'm doing wrong...
Mark your lists with virtual to enable lazy loading. Also is not required to initialize the lists Lessons = new List<Lesson>();