EF Core : how to Insert data releated tables - asp.net-mvc

I'm new to Entity Framework Core and learning.
I have a question: I have three tables with many-to-many relations (Product, ProductCategory, Categories). Everything works fine, I can update, delete and add database.
But I couldn't add Product data to related tables. It just inserts into the Product table, but I want to add Id data to ProductCategories.
Thank you for helping me here.
This is my code:
Generic repository
public class EfCoreGenericRepository<TEntity, TContext> : IRepository<TEntity>
where TEntity : class
where TContext : DbContext, new()
{
public virtual void Create(TEntity entity)
{
using (var context = new TContext())
{
context.Set<TEntity>().Add(entity);
context.SaveChanges();
}
}
public void Delete(TEntity entity)
{
using (var context = new TContext())
{
context.Set<TEntity>().Remove(entity);
context.SaveChanges();
}
}
public List<TEntity> GetAll()
{
using (var context = new TContext())
{
return context.Set<TEntity>().ToList();
}
}
public TEntity GetById(int id)
{
using (var context = new TContext())
{
return context.Set<TEntity>().Find(id);
}
}
public virtual void Update(TEntity entity)
{
using (var context = new TContext())
{
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
}
}
}
ProductRepository
public class EfCoreProductRepository : EfCoreGenericRepository<Product, FoodContext>, IProductRepository
{
public Product GetByIdWithCategories(int id)
{
using (var context = new FoodContext())
{
return context.Products
.Where(p => p.ProductId == id)
.Include(p => p.ProductCategories)
.ThenInclude(pc => pc.Category)
.FirstOrDefault();
}
}
public List<Product> GetProductsByCategory(string name)
{
using (var context = new FoodContext())
{
var products = context.Products.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
products = products.Include(i => i.ProductCategories)
.ThenInclude(i => i.Category)
.Where(i => i.ProductCategories.Any(a => a.Category.Name.ToLower() == name.ToLower()));
}
return products.ToList();
}
}
public void Update(Product entity, int[] categoryIds)
{
using (var context = new FoodContext())
{
var product = context.Products
.Include(i => i.ProductCategories)
.FirstOrDefault(i => i.ProductId == entity.ProductId);
if (product != null)
{
product.Name = entity.Name;
product.Price = entity.Price;
product.ImageUrl = entity.ImageUrl;
product.ProductCategories = categoryIds.Select(catid => new ProductCategory() {
ProductId = entity.ProductId,
CategoryId = catid
}).ToList();
}
context.SaveChanges();
}
}
// I may override Create code here.
}
MVC Post method
[HttpPost]
public async Task<IActionResult> CreateProduct(ProductModel model, IFormFile file)
{
if (ModelState.IsValid)
{
if (file != null)
{
var extension = Path.GetExtension(file.FileName);
var randomName = string.Format($"{DateTime.Now.Ticks}{extension}");
model.ImageUrl = randomName;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\img\\Products", randomName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
var entity = new Product()
{
Name = model.Name,
Price = model.Price,
ImageUrl = model.ImageUrl,
CategoryId = model.CategoryId
};
if (_productService.Create(entity))
{
TempData.Put("message", new AlertMessage
{
Title = $"{entity.Name} named product added successfully",
Message = $"{entity.Name} named product added successfully",
AlertType = "alert-success"
});
return RedirectToAction("ProductList");
};
TempData.Put("message", new AlertMessage
{
Title = _productService.ErrorMessage,
Message = _productService.ErrorMessage,
});
}
ViewBag.Categories = _categoryService.GetAll();
return View(model);
}
You can ask for more code if you need to see it. Thanks for now!
Edit ---> Entity Classes
Category.cs
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public List<ProductCategory> ProductCategories{get; set;}
}
Product.cs
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public double? Price { get; set; }
public bool IsApproved { get; set; }
public int? CategoryId { get; set; }
public string ImageUrl { get; set; }
public List<ProductCategory> ProductCategories{get; set;}
}
ProductCategories.cs
public class ProductCategory
{
public int CategoryId { get; set; }
public Category Category { get; set; }
public int ProductId { get; set; }
public Product Product{get; set;}
}

Thanks to Pirate. I solved problem. I'm new in efcore but i was aware of many to many relationship unnecessary. I had done for many products with many categories but I changed my mind later. Maybe I can use it for next codes.
public override void Create(Product entity)
{
base.Create(entity);
using (var context = new FoodContext())
{
var product = context.Products
.Where(i=>i.ProductId==entity.ProductId)
.Include(i=>i.ProductCategories).FirstOrDefault();
product.ProductCategories.Add(new ProductCategory(){
ProductId=entity.ProductId,
CategoryId=(int)entity.CategoryId
});
context.SaveChanges();
}
}
Note: I don't know how true using double using-code-block

Related

MVC Core How to correctly Edit Existing model using viewmodel

I am trying to Edit existing instance of class project using a viewmodel. I think I have no issues in the GET method of the Edit (everything renders fine on the View) but I am struggling with the POST method. The problem is the POST method creates new instance of the project instead updating the current one.
This is my model:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Budget { get; set; }
public string BusinessCase { get; set; }
public string StartDate { get; set; }
public string FinishDate { get; set; }
public int ClientId { get; set; }
public Client Client { get; set; }
public ICollection<ProjectMember> ProjectMembers { get; set; }
public Project()
{
}
}
}
This is my CreateProjectViewModel (some of the attributes are not used in the controller code below):
public class CreateProjectViewModel
{
//project
public int ProjectId { get; set; }
public string Name { get; set; }
public string Budget { get; set; }
public string BusinessCase { get; set; }
public string StartDate { get; set; }
public string FinishDate { get; set; }
//clients
public int ClientId { get; set; }
public Client Client { get; set; }
public ICollection<ProjectMember> ProjectMembers { get; set; }
//Members
public int MemberId { get; set; }
public Member Member { get; set; }
public Project Project { get; set; }
public List<SelectListItem> Members { get; set; }
public IEnumerable<SelectListItem> Clients { get; set; }
public IEnumerable<int> SelectedMembers { get; set; }
public CreateProjectViewModel() { }
}
}
This is My Edit GET method in ProjectController:
public async Task<IActionResult> Edit(int? id)
{
Project project = await _context.Project
.FirstOrDefaultAsync(m => m.ProjectId == id);
var members = _context.Member
.Select(m => new SelectListItem { Value = m.MemberId.ToString(), Text = m.MemberName }).ToList();
var clients = _context.Client
.Select(r => new SelectListItem { Value = r.ClientId.ToString(), Text = r.Name }).ToList();
CreateProjectViewModel viewmodel = new CreateProjectViewModel
{
Name = project.Name,
Budget = project.Budget,
BusinessCase = project.BusinessCase,
StartDate = project.StartDate,
FinishDate = project.FinishDate,
Project = project,
Clients = clients,
Members = members
};
return View(viewmodel);
This is my Edit POST method in ProjectController which is wrongly creating a new project instead of updating the current project:
(The controller is trying to save values to Project Model and at the same time in Join table containing MemberId and ProjectID - this works fine when creating a project, not sure if correct for updating)
public IActionResult Edit(int? id, CreateProjectViewModel model)
{
Project project = _context.Project
.Single(m => m.ProjectId == id);
//this is to post in many-to-many join table between Member and Project
var projectID = project.ProjectId;
var memberID = model.MemberId;
IList<ProjectMember> existingItems = _context.ProjectMembers
.Where(cm => cm.MemberId == memberID)
.Where(cm => cm.ProjectId == projectID).ToList();
if (existingItems.Count == 0)
{
foreach (var selectedId in model.SelectedMembers)
{
_context.ProjectMembers.Add(new ProjectMember
{
ProjectId = project.ProjectId,
MemberId = selectedId,
});
}
}
//this is to update the values in the project which refers to ProjectID
project.ProjectId = model.ProjectId;
project.Name = model.Name;
project.Budget = model.Budget;
project.BusinessCase = model.BusinessCase;
project.StartDate = model.StartDate;
project.FinishDate = model.FinishDate;
project.ClientId = model.ClientId;
_context.Entry(project).State = EntityState.Modified;
_context.SaveChanges();
return RedirectToAction("Index");
}
Can you guys advise me what should be changed in either method to get the expected result?
Thanks a lot.
After some digging done this is the working code.
Edit POST Method:
public IActionResult Edit(int? id, CreateProjectViewModel viewmodel)
{
if (ModelState.IsValid)
{
var project = _context.Project
.SingleOrDefault(m => m.ProjectId == id);
//this is to update the Project from the viewmodel
project.Name = viewmodel.Name;
project.Budget = viewmodel.Budget;
project.BusinessCase = viewmodel.BusinessCase;
project.StartDate = viewmodel.StartDate;
project.FinishDate = viewmodel.FinishDate;
project.ClientId = viewmodel.ClientId;
//code below is to validate if the matched primary keys of Project and Member are not already in ProjectMembers table
foreach (var selectedId in viewmodel.SelectedMembers)
{
var projectID = project.ProjectId;
var memberID = selectedId;
IList<ProjectMember> existingItems = _context.ProjectMembers
.Where(cm => cm.MemberId == memberID)
.Where(cm => cm.ProjectId == projectID).ToList();
if (existingItems.Count == 0)
{
//this is to add new entry into ProjectMembers table
_context.ProjectMembers.Add(new ProjectMember
{
ProjectId = project.ProjectId,
MemberId = selectedId,
});
}
}
_context.SaveChanges();
}
return RedirectToAction("Index");

How to retrieve a data from database?

I have a question about how to get data from a database.
This is my code so far:
public class TabController : Controller
{
private TestEntities db = new TestEntities();
public ActionResult Index()
{
var model = GetTab();
return View(model);
}
private IEnumerable<MN_REITER> GetReiter()
{
var reiters = Enumerable.Range(0,5).Select(
i => new MN_REITER
{
TabId = i,
TabDescription = "Tab "+i
//REITER_LABEL = "Label" + i
});
return reiters;
}
Like answer a have a Tab0 to Tab5, but I want a get the information from database.
Edit:
View.cshtml
#model IEnumerable >rcMDM.Data.MN_REITER>
(Html.Kendo().TabStrip()
.Name("reiterTab")
.BindTo(Model, (item, reiter)=>
{
item.Text = reiter.REITER_BEZEICHNUNG;
// item.Template.Html = reiter.REITER_LABEL;
})
and the model
public partial class MN_REITER
{
public decimal REITERID { get; set; }
public string REITER_BEZEICHNUNG { get; set; }
public string REITER_LABEL { get; set; }
}
}

Httpget Method using asp.net mvc in controller

Fetch data from database some error occur here (AdminPurpose con = i.a ) Message show Cannot implicitly converted type.Please see below for my code snippets:
public JsonResult GetInfor()
{
List<AdminPurpose> all = new List<AdminPurpose>();;
using (db_Hajj_UmrahEntities dc= new db_Hajj_UmrahEntities()) {
var datas = (from a in dc.Duas join b in dc.Purposes on a.PurposeId equals b.Id
select new {
a,
b.PurPose1
});
if(datas != null) {
foreach (var i in datas)
{
AdminPurpose con = i.a ;
con.PurPose1 = i.PurPose1;
all.Add(con);
}
}
}
return new JsonResult { Data = all, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
Model class one to many relation here it's using
[MetadataType(typeof(DuaMetaData))]
public partial class AdminPurpose
{
public string PurPose1 { get; set; }
}
public class DuaMetaData
{
public string Dua_Name { get; set; }
public string Arabic_Word { get; set; }
public string Translation_English { get; set; }
public string Source { get; set; }
[Display(Name = "Purpose")]
public int PurposeId { get; set; }
}

how to update nested List<Object> to a model in asp.net MVC?

I'm working on a web site that can manipulate some categories and subcategories
my model for each of the is :
public class Category
{
[Key]
public int Categoryid { get; set; }
[Required , StringLength(50)]
public string Categoryname { get; set; }
public virtual List<SubCategory> SubCategories { get; set; }
public static string Serialize(Category cat)
{
string jObject= JsonConvert.SerializeObject(cat);
return jObject;
}
public static Category DeSerialize(string cat)
{
Category Dcat = JsonConvert.DeserializeObject<Category>(cat);
return Dcat;
}
}
--
public class SubCategory
{
[Key]
public int SubCategoryid { get; set; }
[Required, StringLength(50)]
public string SubCategoryname { get; set; }
public int Categoryid { get; set; }
public SubCategory( string name)
{
SubCategoryname = name;
}
public SubCategory()
{
}
}
And my Action that edit/create the subcategories and Categories t is :
[HttpPost]
public ActionResult Edit(int id, string txtSub, string subCreate, string saveAll)
{
Category Cat = context.Categories.Single(cat => cat.Categoryid == id);
UpdateModel(Cat, new string[] { "Categoryname "});
if (ModelState.IsValid)
{
// Create Sub
if (subCreate!= null)
{
if (txtSub != "")
{
context.SubCategories.Add(new SubCategory(txtSub) { Categoryid = Cat.Categoryid });
context.SaveChanges();
}
return RedirectToAction("Edit");
}
if (saveAll!= null)
{
// Edit Sub/Cat
for (int i = 0; i < Cat.SubCategories.Count; i++)
{
context.Entry(Cat.SubCategories[i]).State = EntityState.Modified;
}
context.Entry(Cat).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("index");
}
}
return View();
}
so the Problem is I cant update specifically the name of my SubCategories because it a nested List of
custom object ....
I read I could specify the logic in the modelupdate() but i just cant do it .
I tried to add
this model updater :
UpdateModel(Cat.SubCategories, new string[] { "SubCategories.SubCategoryname" });
this model updater :
for(int i = 0;iCat.SubCategories.Count;i++)
{
UpdateModel(Cat.SubCategories[0], new string[] { "SubCategories.SubCategoryname" });
}
this model updater :
for(int i = 0;iCat.SubCategories.Count;i++)
{
UpdateModel(Cat.SubCategories[0], new string[] { "SubCategories.SubCategoryname" });
}
this model updater :
for(int i = 0;iCat.SubCategories.Count;i++)
{
UpdateModel(Cat.SubCategories[0], new string[] { "SubCategories["+i+"].SubCategoryname" });
}
I know it possible cause when I'm only doing updateModel(Cat) everyting is greatly binded and work
.But for a security reason I want to only doint it on name of the model, not on ids .
all of them didn't work . so im shure im just missing up a little mistake! Thanks
for your TIME!

Exception NullReferenceException in controller (asp.net mvc)

There are Supplier model and User model in my project, every Supplier has a few Users
Supplier model
public class SupplierRow
{
public Guid Id { get; set; }
public string FullName { get; set; }
public bool Subscribed { get; set; }
public bool Active { get; set; }
public int Visits { get; set; }
public List<UserRow> Users { get; set; }
public bool AllInactive
{
get
{
foreach (UserRow ur in Users)
{
if (ur.Status == 1) return false;
}
return true;
}
}
}
and User model
public class UserRow
{
public Guid Id { get; set; }
public string FullName { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Status { get; set; }
public int Role { get; set; }
public Guid SupplierId { get; set; }
}
then I use my models in controller
public ActionResult Grid(bool? active)
{
var suppliers = Context.Suppliers.AsNoTracking()
.WhereIf(active != null, e => e.Active == active)
.Select(e => new SupplierRow
{
Id = e.Id,
FullName = e.FullName,
Active = e.Active,
Visits = e.Visits,
})
.ToList();
List<Guid> supplierIds = new List<Guid>();
foreach (SupplierRow sr in suppliers)
{
supplierIds.Add(sr.Id);
}
var users = Context.Users.AsNoTracking()
.Where(e => supplierIds.Contains(e.SupplierId.Value))
.Select(e => new UserRow
{
Id = e.Id,
FullName = e.FullName,
Email = e.Email,
Name = e.Name,
Status = e.Status,
Role = e.Role,
SupplierId = e.SupplierId.Value
}).ToList();
foreach (UserRow ur in users)
{
foreach (SupplierRow sr in suppliers)
{
if (ur.SupplierId == sr.Id)
{
sr.Users.Add(ur);
}
}
}
return PartialView("_Grid", suppliers);
}
but when I try to debug my project I get some exception here
What's wrong? How can I fix that?
Your Users list are not initialized. Create a new list before accessing it Users = new List<UserRow>(); You can change the SupplierRow class:
public class SupplierRow {
private List<UserRow> users = new List<UserRow>();
public List<UserRow> Users
{
get { return users; }
set { users = value; }
}
...
}
or in the constructor:
public class SupplierRow
{
public SupplierRow()
{
Users = new List<UserRow>();
}
public List<UserRow> Users { get; set; }
...
}
or before accessing it:
foreach (UserRow ur in users)
{
foreach (SupplierRow sr in suppliers)
{
sr.Users = new List<UserRow>();
if (ur.SupplierId == sr.Id)
{
sr.Users.Add(ur);
}
}
}
or you can just use linq:
foreach (SupplierRow sr in suppliers)
{
sr.Users = users.Where(user => user.SupplierId == sr.Id);
}
return PartialView("_Grid", suppliers);

Resources