Many to many insert with repository Entity Framework - asp.net-mvc

My entities:
public class Product : Base.BaseEntity
{
public Product()
{
this.Colors = new HashSet<Color>();
}
[StringLength(50)]
public string ProductName { get; set; }
public int CategoryID { get; set; }
[StringLength(100)]
public string StockFinishes { get; set; }
[StringLength(50)]
public string Guarantee { get; set; }
[StringLength(50)]
public string Installation { get; set; }
public virtual ProductEntity.Category OwnerCategory { get; set; }
public virtual IList<VariationEntity.Variation> Variations { get; set; }
public virtual ICollection<ProductEntity.Color> Colors { get; set; }
}
public class Color : Base.BaseEntity
{
public Color()
{
this.Products = new HashSet<Product>();
}
[StringLength(50)]
public string ColorName { get; set; }
public virtual ICollection<ProductEntity.Product> Products { get; set; }
}
My controller:
[HttpPost]
public ActionResult NewProduct(Models.DTO.ProductDTO.ProductVM productmodel)
{
if (ModelState.IsValid)
{
DATA.Models.ORM.Entity.ProductEntity.Product productentity = new DATA.Models.ORM.Entity.ProductEntity.Product();
productentity.ProductName = productmodel.ProductName;
productentity.CreatedBy = User.UserId;
productentity.CategoryID = productmodel.CategoryID;
productentity.StockFinishes = productmodel.StockFinishes;
productentity.Guarantee = productmodel.Guarantee;
productentity.Installation = productmodel.Installation;
rpproduct.Insert(productentity);
rpproduct.SaveChanges();
if (productmodel.SelectedColors != null)
{
foreach (var colorId in productmodel.SelectedColors)
{
DATA.Models.ORM.Entity.ProductEntity.Color color = rpcolor.FirstOrDefault(x => x.Id == colorId);
productentity.Colors.Add(color);
}
db.SaveChanges();
}
return RedirectToAction("ProductList");
}
else
{
ViewBag.Error = "An error occurred while adding a new product";
return View();
}
}
There is no error, but product colors are not inserted into the database. I can't do it with repository.
How can I add product colors with repository or without repository?
Sorry for bad English :(

I prefer to manage many to many relations myself not Entity Framework.
In my view you need another table to store colors of products with columns ColorId and ProductId. Then there should be a DbSet on your DbContext.
After that you can save a new entity ProductColors which stores ColorId and ProductId. Your entities Color and Product can have a reference to this table, not to Color and Product table.
public class ProductColor : Base.BaseEntity
{
public ProductColor()
{
}
public int ColorId { get; set; }
public virtual Color Color { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}

Related

Unsure how to insert item into database using Entity Framework with many to many relationship

I am trying to insert a product into my database with an associated category. One product can belong to several categories and obviously one category can have several products. When I insert, I am sure I am missing something in my controller method but I'm not sure what it is. I have a bridge table called ProductCategory that just has a ProductID and a CategoryID in it. That table is not getting populated when I do the insert.
Here is my controller method that is doing the insert:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditProduct([Bind(Include = "ID,itemNumber,product,description,active,PDFName,imageName,SelectedCategories")] ProductModel model)
{
if (ModelState.IsValid)
{
using (var context = new ProductContext())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
if (model.ID == 0)
{
// Since it didn't have a ProductID, we assume this
// is a new Product
if (model.description == null || model.description.Trim() == "")
{
model.description = "Our Famous " + model.product;
}
if (model.imageName == null || model.imageName.Trim() == "")
{
model.imageName = model.itemNumber + ".jpg";
}
if (model.PDFName == null || model.PDFName.Trim() == "")
{
model.PDFName = model.itemNumber + ".pdf";
}
Session["dropdownID"] = model.ID;
// I think I probably need some additional code here...
context.Products.Add(model);
}
else
{
// Since EF doesn't know about this product (it was instantiated by
// the ModelBinder and not EF itself, we need to tell EF that the
// object exists and that it is a modified copy of an existing row
context.Entry(model).State = EntityState.Modified;
}
context.SaveChanges();
return RedirectToAction("ControlPanel");
}
}
return View(model);
}
And my Product model:
public class ProductModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Index("ItemNumber", 1, IsUnique = true)]
[Display(Name = "Item #")]
public int itemNumber { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Product")]
[MaxLength(50)]
public String product { get; set; }
[Display(Name = "Description")]
[MaxLength(500)]
public String description { get; set; }
[DefaultValue(true)]
[Display(Name = "Active?")]
public bool active { get; set; }
[Display(Name = "Image Name")]
public String imageName { get; set; }
[Display(Name = "PDF Name")]
public String PDFName { get; set; }
[Display(Name = "Category(s)")]
public virtual ICollection<CategoryModel> ProductCategories { get; set; }
public int[] SelectedCategories { get; set; }
public IEnumerable<SelectListItem> CategorySelectList { get; set; }
//public ICollection<CategoryModel> CategoryList { get; set; }
public virtual BrochureModel Brochure { get; set; }
public IEnumerable<SelectListItem> BrochureList { get; set; }
[Display(Name = "Category(s)")]
public String CategoryList { get; set; }
public static IEnumerable<SelectListItem> getCategories(int id = 0)
{
using (var db = new ProductContext())
{
List<SelectListItem> list = new List<SelectListItem>();
var categories = db.Categories.ToList();
foreach (var cat in categories)
{
SelectListItem sli = new SelectListItem { Value = cat.ID.ToString(), Text = cat.categoryName };
//if (id > 0 && cat.ID == id)
//{
// sli.Selected = true;
//}
list.Add(sli);
}
return list;
}
}
public ProductModel()
{
active = true;
}
}
And my Category model:
public class CategoryModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Category Name")]
[MaxLength(50)]
public String categoryName { get; set; }
[MaxLength(50)]
public String categoryDBName { get; set; }
[DefaultValue(true)]
[Display(Name = "Active?")]
public bool isActive { get; set; }
//public virtual ICollection<ProductCategory> ProductCategories { get; set; }
public virtual ICollection<ProductModel> Products { get; set; }
}
Here is my Product context:
public class ProductContext : DbContext
{
public ProductContext()
: base("DefaultConnection")
{
Database.SetInitializer<ProductContext>(new CreateDatabaseIfNotExists<ProductContext>());
}
public DbSet<CategoryModel> Categories { get; set; }
public DbSet<ProductModel> Products { get; set; }
public DbSet<BrochureModel> Brochures { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
//modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Entity<CategoryModel>().ToTable("Categories");
modelBuilder.Entity<ProductModel>().ToTable("Products");
modelBuilder.Entity<BrochureModel>().ToTable("Brochures");
modelBuilder.Entity<ProductModel>()
.HasMany(p => p.ProductCategories)
.WithMany(p => p.Products)
.Map(m =>
{
m.ToTable("ProductCategory");
m.MapLeftKey("ProductID");
m.MapRightKey("CategoryID");
});
//modelBuilder.Entity<CategoryModel>()
//.HasMany(c => c.ProductCategories)
//.WithRequired()
//.HasForeignKey(c => c.CategoryID);
}
public System.Data.Entity.DbSet<newBestPlay.Models.RegisterViewModel> RegisterViewModels { get; set; }
}
Let me know if other code or more info is needed.
You're never doing anything with your SelectedCategories array. You need to use this to pull CategoryModel instance from the database and then associate those with the product.
context.Categories.Where(c => model.SelectedCategories.Contains(c.ID)).ToList()
.ForEach(c => model.ProductCategories.Add(c));
...
context.SaveChanges();
UPDATE
Can I ask how to list out the categories for each product in my view?
That's kind of a loaded question, as it's highly dependent on what type of experience you're trying to achieve. Generally speaking, with any collection, you'll need to iterate over the items in that collection and then render some bit of HTML for each item. You can do this in a number of different ways, which is why there's not really one "right" answer I can give you. However, just to give you an idea and not leave you with no code at all, here's a very basic way to just list out the name of every category:
#string.Join(", ", Model.ProductCategories.Select(c => c.categoryName))

MVC Accessing navigation property for listboxFor

At first I should say i am compeletely newbie in MVC.
I have 3 Objects
public partial class Magazine
{
public Magazine()
{
this.NumberTitles = new HashSet<NumberTitle>();
}
public int Id { get; set; }
public int MagYear { get; set; }
public int MagNo { get; set; }
public int MagSeason { get; set; }
public string MagYear2 { get; set; }
public virtual ICollection<NumberTitle> NumberTitles { get; set; }
}
public partial class NumberTitle
{
public NumberTitle()
{
this.Articles = new HashSet<Article>();
}
public int Id { get; set; }
public int MagazineId { get; set; }
public int TitleId { get; set; }
public int position { get; set; }
public virtual ICollection<Article> Articles { get; set; }
public virtual Magazine Magazine { get; set; }
public virtual Title Title { get; set; }
}
public partial class Title
{
public Title()
{
this.ChildrenTitle = new HashSet<Title>();
this.NumberTitles = new HashSet<NumberTitle>();
}
public int Id { get; set; }
public string TitleText { get; set; }
public Nullable<int> ParentId { get; set; }
public virtual ICollection<Title> ChildrenTitle { get; set; }
public virtual Title ParentTitle { get; set; }
public virtual ICollection<NumberTitle> NumberTitles { get; set; }
}
In a View I want to have TextBox to show Magazine Number and 2 List boxes. one shows all the Available Titles and the Other just selected Titles for that Magazine Number.So I have made View Model
public class NumberTitleViewModel
{
public Magazine Magazine { get; set; }
public List<NumberTitle> NumberTitles { get; set; }
}
this is in controller. how can i get the list of titles for specified MagazineId
public ActionResult EditTitle(int id)
{
Func<IQueryable<Magazine>, IOrderedQueryable<Magazine>> orderByFunc = null;
Expression<Func<Magazine, bool>> filterExpr = null;
if (id>0)
{
filterExpr = p => p.Id.Equals(id);
}
Magazine magazine = unitOfWork.MagazineRepository.Get(filter: filterExpr, orderBy: orderByFunc, includeProperties: "").SingleOrDefault();
NumberTitleViewModel numberTitleViewMode = new NumberTitleViewModel();
numberTitleViewMode.Magazine = magazine;
Expression<Func<NumberTitle, bool>> filterExpr2 = null;
if (id > 0)
{
filterExpr2 = p => p.MagazineId.Equals(id);
}
var numberTitles = unitOfWork.NumberTitleRepository.Get(filterExpr2, null, includeProperties: "Title").ToList();
var titles = unitOfWork.TitleRepository.Get(null, null, "");
numberTitleViewMode.NumberTitles = numberTitles; ///this part doesn't show the Titles. how should access the TitleName not Id
ViewBag.titles = new SelectList(titles, "Id", "TitleText");
return View("../Panel/Magazine/EditTitle", "_BasicLayout", numberTitleViewMode);
}
Not sure what you have in your view but you should have something like:
#using NameSpace.Models
#model NameSpace.Models.NumberTitleViewModel
Then you can do something like this in your view:
foreach (NumberTitles item in #Model)
{
<label>#item.Title.TitleText</label>
}
Not exact but should get you close to what you need

Saving Viewmodel data to the Database in ASP.NET MVC

I am new to ASP.net MVC and am using a viewmodel rather than viewbags to populate my dropdowns since I've seen most people recommend against them. I have a slick UI that does cascading dropdowns and autocompletes (not shown here) but I can't seem to get my data saved back to the database.
Models:
public partial class Car
{
public int CarID { get; set; }
public string CarName { get; set; }
public int ModelID { get; set; }
public int ManufacturerID { get; set; }
public int CarColorID { get; set; }
public Nullable<decimal> Price { get; set; }
public string Description { get; set; }
public virtual CarColor CarColor { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public virtual CarModel CarModel { get; set; }
}
public partial class CarColor
{
public CarColor()
{
this.Cars = new HashSet<Car>();
}
public int ColorID { get; set; }
public string ColorName { get; set; }
public virtual ICollection<Car> Cars { get; set; }
}
public partial class CarModel
{
public CarModel()
{
this.Cars = new HashSet<Car>();
}
public int CarModelID { get; set; }
public int ManufacturerID { get; set; }
public string CarModelName { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
}
public partial class Manufacturer
{
public Manufacturer()
{
this.Cars = new HashSet<Car>();
this.Manufacturer1 = new HashSet<Manufacturer>();
this.CarModels = new HashSet<CarModel>();
}
public int ManufacturerID { get; set; }
public string ManufacturerName { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual ICollection<Manufacturer> Manufacturer1 { get; set; }
public virtual Manufacturer Manufacturer2 { get; set; }
public virtual ICollection<CarModel> CarModels { get; set; }
}
ViewModel:
public class AnotherTestViewModel
{
public Car car { get; set; }
public IEnumerable<SelectListItem> CarModels { get; set; }
public IEnumerable<SelectListItem> Manufacturers { get; set; }
public IEnumerable<SelectListItem> CarColors { get; set; }
}
Controller:
public ActionResult Create()
{
var model = new AnotherTestViewModel();
using (new CarTestEntities())
{
model.CarModels = db.CarModels.ToList().Select(x => new SelectListItem
{
Value = x.CarModelID.ToString(),
Text = x.CarModelName
});
model.Manufacturers = db.Manufacturers.ToList().Select(x => new SelectListItem
{
Value = x.ManufacturerID.ToString(),
Text = x.ManufacturerName
});
model.CarColors = db.CarColors.ToList().Select(x => new SelectListItem
{
Value = x.ColorID.ToString(),
Text = x.ColorName
});
}
return View(model);
}
//
// POST: /AnotherTest/Create
[HttpPost]
public ActionResult Create(AnotherTestViewModel model)
{
if (ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Details", "AnotherTestViewModel", new { id = model.car.CarID });
}
return View();
}
I saw a few recommendations to use Automapper because EntityState.Modified won't work, but I'm not sure how to configure it because using the code below didn't work.
Mapper.CreateMap<AnotherTestViewModel, Car>();
Mapper.CreateMap<Car, AnotherTestViewModel>();
var newCar = Mapper.Map<AnotherTestViewModel, Car>(model);
Any ideas?
Your view model should not be interacting with the database. View Models should only be used in the presentation layer (user interface) - hence the term "View" model. You should have another model (data model) that interacts with your database. Then you should have some type of service layer that handles your conversion between your view model and your data model (and vice versa). Your data model is the model generated by Entity Framework (which I assume is what you are using). To handle updates to your database, you need to instantiate a data context, grab the data entity from your database, make changes to that entity, and call save changes all in that data context. The data context will keep track of all changes to your entities and apply the necessary changes to your database when you call "save changes".
Example:
public void UpdateCar(CarViewModel viewModel)
{
using (DataContext context = new DataContext())
{
CarEntity dataModel = context.CarEntities.where(x => x.Id == viewModel.Id).First();
dataModel.Name = viewModel.Name;
dataModel.Type = viewModel.Type;
context.SaveChanges();
}
}
In this example, context will keep track of any changes to "dataModel". When "context.SaveChanges" is called, those changes will automatically be applied to the database.

Model or ViewModel in a ListViewModel

Should T be a for example Customer or CustomerViewModel ?
The annotations bound to Mvc namespace are on the ListViewModel so actually I could pass the Customer object. What do you think?
public class ListViewModel<T>
{
[Required(ErrorMessage="No item selected.")]
public int[] SelectedIds { get; set; }
public IEnumerable<T> DisplayList { get; set; }
}
UPDATE
[HttpGet]
public ActionResult Open()
{
IEnumerable<Testplan> testplans = _testplanDataProvider.GetTestplans();
OpenTestplanListViewModel viewModel = new OpenTestplanListViewModel(testplans);
return PartialView(viewModel);
}
public class OpenTestplanListViewModel
{
public OpenTestplanListViewModel(IEnumerable<Testplan> testplans)
{
var testplanViewModels = testplans.Select(t => new TestplanViewModel
{
Name = string.Format("{0}-{1}-{2}-{3}", t.Release.Name, t.Template.Name, t.CreatedAt, t.CreatedBy),
TestplanId = t.TestplanId,
});
DisplayList = testplanViewModels;
}
[Required(ErrorMessage = "No item selected.")]
public int[] SelectedIds { get; set; }
public string Name { get; set; }
public IEnumerable<TestplanViewModel> DisplayList { get; private set; }
}
public class TestplanViewModel
{
public int TestplanId { get; set; }
public string Name { get; set; }
}
public class Testplan
{
public int TestplanId { get; set; }
public int TemplateId { get; set; }
public int ReleaseId { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public Template Template { get; set; }
public Release Release { get; set; }
}
T should ideally be a view model. Having a view model referencing domain models is some kind of a hybrid view model, not a real one. But if you think that in this specific case the domain model will be exactly the same as the view model then you could keep it as well.

entity framework many to many inserts code first

this is my model
public class Post
{
public long PostID { get; set; }
[Required]
[MaxLength(255)]
public string Title { get; set; }
}
public class Tag
{
public long TagID { get; set; }
[Required]
[Display(Name = "Tag Name")]
[MaxLength(30)]
public string TagName { get; set; }
public bool IsActive { get; set; }
}
public class TagPost
{
public long TagPostID { get; set; }
public long PostID { get; set; }
public long TagID { get; set; }
[ForeignKey("PostID")]
public virtual Post Posts { get; set; }
[ForeignKey("TagID")]
public virtual Tag Tags { get; set; }
}
1) Is this the right many to many configuration in EF 4.1 without mentioning the modelbinder for many to many.
2) if i have completed the many to many configuration using dataannotation why the data is not inserting in tagpost .
public void InsertPostQuestion(Post post,List<string> tags)
{
context.Posts.Add(post);
foreach (string tag in tags)
{
Tag tagr = new Tag();
tagr.TagName = tag;
tagr.IsActive = true;
context.Tags.Add(tagr);
}
context.SaveChanges();
}
3) I do have to define modelbinder to have many to many inserts or delete or update?
modelBuilder.Entity<Post>().
HasMany(c => c.Tags).
WithMany(p => p.Posts).
Map(
m =>
{
m.MapLeftKey("PostID");
m.MapRightKey("TagID");
m.ToTable("TagPost");
});
Change your models to this:
public class Post
{
public long PostID { get; set; }
[Required]
[MaxLength(255)]
public string Title { get; set; }
public bool IsActive { get; set; }
public virtual List<Tag> Tags { get; set; }
}
public class Tag
{
public long TagID { get; set; }
[Required]
[Display(Name = "Tag Name")]
[MaxLength(30)]
public string TagName { get; set; }
public bool IsActive { get; set; }
public virtual List<Post> Posts { get; set; }
}
and then save like so:
public void InsertPostQuestion(Post post,List<string> tags)
{
context.Posts.Add(post);
foreach (string tag in tags)
{
// TODO: If tag has a unique index on TagName, see if it exists first
Tag tagr = new Tag();
tagr.TagName = tag;
tagr.IsActive = true;
context.Tags.Add(tagr);
post.Tags.Add(tagr);
}
context.SaveChanges();
}
EF will create the intermediate table in the db and populate it nicely automatically.

Resources