MVC NET 5 linked model saves to database but doesn't load - c#

I'm fairly new to .NET MVC 5, coming from a primarily PHP background. I have a 'Project' model with a List collection in it. When I use just a test controller to save data into the collection, it will save into the database just fine.
The problem arises when I'm trying to pull information out of that collection, as it always comes back as null.
My Project model:
public class Project
{
public int ID { get; set; }
[Display(Name = "Project")]
public string ProjectNumber { get; set; }
[Display(Name = "Client")]
public string Client { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Display(Name = "Start Date")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime StartDate { get; set; }
[Display(Name = "Required Date")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime RequiredDate { get; set; }
public string ProjectManagerId { get; set; }
[ForeignKey("ProjectManagerId")]
[Display(Name = "Project Manager")]
public ApplicationUser ProjectManager { get; set; }
[Display(Name = "Project Members")]
public int[] ProjectMembers { get; set; }
[Display(Name = "Tasks")]
public List<ProjectTask> TaskCollection { get; set; }
}
public class ProjectDb : DbContext
{
public ProjectDb() : base("DefaultConnection")
{
}
public DbSet<Project> Projects { get; set; }
}
My ProjectTask model:
public class ProjectTask
{
public int Id { get; set; }
public int ProjectId { get; set; }
[ForeignKey("ProjectId")]
public virtual Project Project { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string TaskNotes { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime StartDate { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime DueDate { get; set; }
public int Duration { get; set; }
public TaskStatus Status { get; set; }
public TaskAlertLevel AlertLevel { get; set; }
public List<ProjectTask> SubTasks { get; set; }
public string Progress { get; set; }
}
public class ProjectTaskDb : DbContext
{
public ProjectTaskDb() : base("DefaultConnection")
{
}
public DbSet<Project> ProjectTasks { get; set; }
}
My controller where I'm just trying to see something other than null exceptions:
public ActionResult DetailTasks(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Project project = db.Projects.Find(id);
if (project == null)
{
return HttpNotFound();
}
var tasks = project.TaskCollection;
ViewBag.tasks = tasks;
return View(project);
}
Hopefully it's something completely obvious and stupid that escapes me right now!

Apologies in regards to the comments, for some reason I thought you were having issues loading up the initial project object. So looking at your Project entities you have lazy loading turned off because you are not using the virtual keyword:
public List<ProjectTask> TaskCollection { get; set; }
vs
public virtual List<ProjectTask> TaskCollection { get; set; }
When you do this you must either Explicitly or Eagerly load your TaskCollection
Exlicitly load:
Project project = db.Projects.Find(id);
db.Entry(project).List(a => a.TaskCollection).Load();
Eagerly Load:
Project project = db.Projects.where(a => a.ID == id).Include(b => b.TaskCollection).FirstOrDefault();
p.s. you may have to play with my context a bit as I'm not at a place where I can load-up VS

Hi you can try with changing this line: var tasks = project.TaskCollection; to like this: var tasks = project.TaskCollection.ToList();, it may help you
Full example/Changed Code:
public ActionResult DetailTasks(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Project project = db.Projects.Find(id);
if (project == null)
{
return HttpNotFound();
}
var tasks = project.TaskCollection.ToList();
ViewBag.tasks = tasks;
return View(project);
}
I believe this time you wont get null for the project.TaskCollection , if the data available in the database.
Hope the above the information was useful , kindly let me know your thoughts or feedbacks
Thanks
Karthik

Related

EF Core & ASP.NET Core MVC add new data instead update

I am trying to update some content and my codes just adding new row instead updating data. I am already using addscoped in startup.cs. (Previous answers were about this.)
I am using repository pattern.
For the answer i take previously ; OF COURSE i have database and all the parts i need. I am doing insert, delete without problem.
Here is my entity:
public class Product : IEntity
{
public int Id { get; set; }
[Required(ErrorMessage ="Name can not be empty.")]
[MinLength(3,ErrorMessage ="Lenght must be at least 3 characters.")]
public string Name { get; set; }
[DataType(DataType.Currency)]
public double Price { get; set; }
public double? Discount { get; set; }
public string Description { get; set; }
public DateTime? CreatedTime { get; set; }
public DateTime? ModifiedDate { get; set; }
[DisplayName("Image URL")]
public string ImageUrl { get; set; }
public bool IsActive { get; set; } = true;
public Team Team { get; set; }
public int? TeamId { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
}
Here is my repo. for updating :
public async Task<int> Update(Product entity)
{
entity.ModifiedDate = DateTime.Now;
dbContext.Update(entity);
return await dbContext.SaveChangesAsync();
}
Here is the controller : DD methods for view. Showing category/team names instead their id on dropdowns.
[HttpPost]
public async Task<IActionResult> Edit(ProductUpdateRequest productToUpdate)
{
if (ModelState.IsValid)
{
int affectedRows= await productService.UpdateProduct(productToUpdate);
if (affectedRows>0)
{
TempData["RowAlert"] = "Edit successfully performed.";
}
return View();
}
TempData["RowAlert"] = "Your entries are not acceptable. Check your properties.";
List<SelectListItem> selectedCategories = new List<SelectListItem>();
selectedCategories = await getCategoriesforDD();
List<SelectListItem> selectedTeams = new List<SelectListItem>();
selectedTeams = await getTeamsforDD();
ViewBag.Categories = selectedCategories;
ViewBag.Teams = selectedTeams;
return View();
}
As by your previous comment, this should work:
public async Task<int> Add(Product entity)
{
context.Entry(entity).State = EntityState.Added;
return await dbContext.SaveChangesAsync();
}
More info here: https://learn.microsoft.com/en-us/ef/ef6/saving/change-tracking/entity-state
I solved the issue by adding an ID to my DTO. (Primary key of the entity.)
public class ProductUpdateRequest
{
public int Id { get; set; }
[Required(ErrorMessage = "Ürün adı boş bırakılamaz.")]
public string Name { get; set; }
[DataType(DataType.Currency)]
public double Price { get; set; }
public double? Discount { get; set; }
public bool IsActive { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public int? TeamId { get; set; }
public int CategoryId { get; set; }
}

How to exclude some column while model binding in ASP.NET Core

I was working on an ASP.NET MVC project where I have to bind a view model to a view but I don't want the column Tracking_Id to be edited by the user, at first I avoided that by using
[Bind("Id,ContratId,OrganizationName,ContratDate,StartDate,EndDate,DateUploaded,MediumName,LastEditorUserId")]
on my post handler action parameter but I was forced to use a view model class and I couldn't do that anymore since I can only use that attribute on a parameter. The problem is if I modify the rendered website input field value and "name" to contrat.Tracking_Id and submit the form then it updates the Tracking_Id column and I'm afraid that might cause a security issue. I'm still new to the framework and thanks in advance.
The Contrat class:
public class Contrat
{
[Key]
public int Id { get; set; }
public string ContratId { get; set; }
[Required]
public string OrganizationName { get; set; }
//[Required]
[DataType(DataType.Date)]
public DateTime ContratDate { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[DataType(DataType.Date)]
[Required]
public DateTime EndDate { get; set; }
[Required]
public DateTime DateUploaded { get; set; }
[Required]
public string documentPath { get; set; }
[DisplayName("Medium")]
public string MediumName { get; set; }
[ForeignKey("MediumName")]
public virtual Medium Medium { get; set; }
[DisplayName("Last Edited by")]
public string LastEditorUserId { get; set; }
[ForeignKey("LastEditorUserId")]
public virtual ApplicationUser User { get; set; }
public string TrackingID { get; set; }
}
The view model:
public class ContratUpsertVM
{
[DisplayName("Contrat Document")]
public IFormFile documentPath { get; set; }
public Contrat contrat { get; set; }
}
You can use custom modelbinding to exclude the TrackingID property.
CustomModelBinder.cs:
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model = new Contrat();
var properties = bindingContext.ModelMetadata.Properties;
foreach(var property in properties)
{
var valueresult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "." + property.Name).FirstOrDefault();
if (valueresult != null)
{
if(property.Name != "TrackingID")
{
if (property.ModelType == typeof(DateTime))
{
model.GetType().GetProperty(property.Name).SetValue(model, Convert.ToDateTime(valueresult));
}else if(property.ModelType == typeof(Int32))
{
model.GetType().GetProperty(property.Name).SetValue(model, int.Parse(valueresult));
}
else
{
model.GetType().GetProperty(property.Name).SetValue(model, valueresult);
}
}
}
}
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Then set it on the Contrat property.
public class ContratUpsertVM
{
[DisplayName("Contrat Document")]
public IFormFile documentPath { get; set; }
[ModelBinder(BinderType = typeof(CustomModelBinder))]
public Contrat contrat { get; set; }
}

ASP.NET Relation between two tables

I'm new in Asp.Net and Entity Framework and i have a little issue.
I know u can help me , because its simple and i only know how to do it in PHP XD
I Have 2 models
public class Suppliers
{
public int ID { get; set; }
public int ID_Guard { get; set; }
public string Supplier{ get; set; }
public string Description { get; set; }
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd hh:mm tt}", ApplyFormatInEditMode = true)]
public DateTime Enter { get; set; }
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd hh:mm tt}", ApplyFormatInEditMode = true)]
public DateTime Exit { get; set; }
public virtual Guard Guard { get; set; }
}
public class Guard
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Suppliers> Suppliers{ get; set; }
}
controller
public class SuppliersController : Controller
{
private SuppliersContext db = new SuppliersContext();
public ActionResult Index()
{
return View(db.Suppliers.ToList());
}
}
And i wanna pass to the view the 2 table data and relate it
When i go to index show me all the suppliers data and show the Guard name( the person who registered the supplier enter and exit)
Well its solved
var results = from c in db.Suppliers
join cn in db.Guard on c.ID_Guard equals cn.ID
where (c.ID_Guard == cn.ID)
select new Models.MyViewModel{ Name= cn.Name, ID = c.ID, Supplier=c.Supplier, Description=c.Description, Enter=c.Enter, Exit=c.Exit};
follow the below code it works and seems same like yours also i added the link hope you find It helpful
public class Product
{
public Product() { Id = Guid.NewGuid(); }
public Guid Id { get; set; }
public string ProductName { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
}
public class ProductCategory
{
public int Id { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
/////view model///
public class ProductViewModel
{
public Guid Id { get; set; }
[Required(ErrorMessage = "required")]
public string ProductName { get; set; }
public int SelectedValue { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
[DisplayName("Product Category")]
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
}
follow the below tutorial
relationship model
for basic join use below query
var dealercontacts = from contact in table1
join table2 in table2 on contact.Id equals another.ID
select contact;

.NET EF MVC not loading property

I've been trying to get something done in .NET EF for some time now.
My model looks like this:
public class Route
{
[Key]
public int RouteId { get; set; }
[Display(Name = "Rope")]
public int ropeId { get; set; }
public virtual Rope rope { get; set; }
[Display(Name = "Routesetter")]
public int routeSetterId { get; set; }
public virtual RouteSetter routeSetter { get; set; }
[Display(Name = "Secundary routesetter")]
public int? secundaryRouteSetterId { get; set; }
public virtual RouteSetter secundaryRouteSetter { get; set; }
[Display(Name = "Grade")]
public int gradeId { get; set; }
public virtual ClimbingGrade grade { get; set; }
[Display (Name = "Routename")]
public string name { get; set; }
[Display(Name = "Color")]
public int colorId { get; set; }
public virtual GripColor color { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
[Display (Name ="Date set")]
public DateTime dateSet { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
[Display (Name ="Date removed")]
public DateTime? dateRemoved { get; set; }
[Display (Name ="Marked for removal")]
public bool markedForRemoval { get; set; }
}
The controller:
public ActionResult Index()
{
var routes = db.Routes.Include(r => r.color).Include(r => r.grade).Include(r => r.rope).Include(r => r.routeSetter).Include(r => r.secundaryRouteSetter);
ViewBag.Page = "Routes";
return View(routes.ToList());
}
Now the color property is always null. while all other properties are loaded.
I can't seem to figure this one out. Any help would be welcome
EDIT
The GripColor clas:
public class GripColor
{
[Key]
public int GripColorId { get; set; }
[Display(Name = "Primary color")]
public string primaryColor { get; set; }
[Display(Name = "Secundary color")]
public string secundaryColor { get; set; }
public string displayName { get { return primaryColor + ' ' + secundaryColor; } }
public virtual List<Route> Routes { get; set; }
}
I assume the gripColors are OK in the database;
ViewBag.ColorID = new SelectList(db.Colors, "GripColorId", "displayName");
The above is used to populate a dropdown (wich works) And the colorId is stored correctly in the database
There is no foreign key relating the two tables, then how are you expecting to make inner joins between them?You should setup the relationship between these two tables and define the foreign key.You can use data annotations for this purpose or i highly recommend using fluent api.Check out this documentation for more info:
http://docs.efproject.net/en/latest/modeling/index.html

ASP.NET MVC: Null Foreign Key ID leads to Validation Errors

Short Story:
I load a model instance from db, apply changes to it but when I want to update the changes, Validation exceptions happens for foreign key items which set as [Required] in model definition. I've found a work around but I don't know what is the right way to fix this?
Details:
I have a model as below:
public class Project
{
public int Id { get; set; }
[Required]
[StringLength(100, MinimumLength = 5]
public string Name { get; set; }
[Required]
public virtual ApplicationUser Client { get; set; }
[Key]
[ForeignKey("Client")]
public string ClientID;
[Required]
public virtual ApplicationUser ProjectManager { get; set; }
[Key]
[ForeignKey("ProjectManager")]
public string ProjectManagerID;
[Range(0,100)]
[Required]
public int Progress { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreateDate { get; set; }
[Column("Disabled")]
public bool Disabled{ get; set; }
[Column("Status")]
public string Status{ get; set; }
}
Whenever I fetch model information from db into an instance an apply changes to it, I face exceptions which are about required ProjectManager and Client.
Project currentProject = (from prj in dbContext.Projects
where prj.Id == project.Id).Single();
//---> currentProject.ProjectManagerID is null
//---> currentProject.ClientID is null
//---> currentProject.ProjectManager is present
//---> currentProject.Client is present
currentProject.Name = ....
currentProject.Progress = ....
currentProject.Status = ....
currentProject.Disabled = ....
dbContext.Entry(currentProject).State = System.Data.Entity.EntityState.Modified;
dbContext.SaveChanges(); //--> Validation error for required Client,ProjectManager
This is caused by the fact that when I ftech information from DB, ClientID and ProjectManagerID are null and if I set them manually as below, it will fix:
currentProject.Name = project.Name;
currentProject.Progress = project.Progress;
currentProject.Status = project.Status;
currentProject.Disabled = project.Disabled;
currentProject.ProjectManagerID = currentProject.ProjectManager.Id;
currentProject.ClientID = currentProject.Client.Id;
try
{
dbContext.Entry(currentProject).State = System.Data.Entity.EntityState.Modified;
dbContext.SaveChanges();
}
catch (DbEntityValidationException e)
{
addValidationErrorsToModelState(e);
return View(project);
}
I'm suspicious to virtual settings but not sure, because foreign objects are loaded but their ID is null.
P.S. I declared projects in AppDbContext as below:
public DbSet<Project> Projects { get; set; }
The problem was that I missed setter/getter for ID properties of ForeignKeys:
public class Project
{
public int Id { get; set; }
[Required]
[StringLength(100, MinimumLength = 5]
public string Name { get; set; }
[Required]
public virtual ApplicationUser Client { get; set; }
[ForeignKey("Client")]
public string ClientID{ get; set; } //<--fixed
[Required]
public virtual ApplicationUser ProjectManager { get; set; }
[ForeignKey("ProjectManager")]
public string ProjectManagerID { get; set; } //<--fixed
[Range(0,100)]
[Required]
public int Progress { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreateDate { get; set; }
[Column("Disabled")]
public bool Disabled{ get; set; }
[Column("Status")]
public string Status{ get; set; }
}

Categories

Resources