Unable to access the navigation property of a newly created object - entity-framework

I am working on an asp.net core MVC framework + entity framework. where i got those model classes:-
public partial class Submission
{
public Submission()
{
SubmissionQuestionSubmission = new HashSet<SubmissionQuestionSubmission>();
}
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? Created { get; set; }
public virtual ICollection<SubmissionQuestionSubmission> SubmissionQuestionSubmission { get; set; }
}
public partial class SubmissionQuestion
{
public SubmissionQuestion()
{
SubmissionQuestionSubmission = new HashSet<SubmissionQuestionSubmission>();
}
public int Id { get; set; }
public string Question { get; set; }
public virtual ICollection<SubmissionQuestionSubmission> SubmissionQuestionSubmission { get; set; }
}
public partial class SubmissionQuestionSubmission
{
public int SubmissionQuestionId { get; set; }
public long SubmissionId { get; set; }
public bool? Answer { get; set; }
public virtual Submission Submission { get; set; }
public virtual SubmissionQuestion SubmissionQuestion { get; set; }
}
public class SubmissionCreate
{
public Submission Submission {set; get;}
public IList<SubmissionQuestion> SubmissionQuestion { set; get; }
public IList<SubmissionQuestionSubmission> SubmissionQuestionSubmission { set; get; }
}
and i have the following create view:-
#for (var i = 0; i < Model.SubmissionQuestion.Count(); i++)
{
<div class="form-group">
<input asp-for="#Model.SubmissionQuestion[i].Question" hidden />
<input asp-for="#Model.SubmissionQuestionSubmission[i].SubmissionQuestionId" hidden />
<label class="control-label" style="font-weight:bold">#Model.SubmissionQuestion[i].Question</label><br />
<input type="radio" asp-for="#Model.SubmissionQuestionSubmission[i].Answer" value="true" /><span style="color: #4d9b84;font-size:14px;font-weight:bold"> Yes</span><br />
<input type="radio" asp-for="#Model.SubmissionQuestionSubmission[i].Answer" value="false" /><span style="color: #4d9b84;font-size:14px;font-weight:bold"> No</span>
</div>
}
and the following create post method:-
public async Task<IActionResult> Create([Bind("Submission,SubmissionQuestionSubmission")] SubmissionCreate sc )
{
if (ModelState.IsValid)
{
var newsubmission = _context.Submission.Add(sc.Submission);
sc.Submission.Created = DateTime.Now;
await _context.SaveChangesAsync();
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.SubmissionId = sc.Submission.Id;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
but inside my action method if i try the following sc.Submission.SubmissionQuestionSubmission.FirstOrDefault(a => a.SubmissionQuestion.Question.StartsWith("Are you")).Answer i will get null reference exception where the sc.Submission.SubmissionQuestionSubmission.SubmissionQuestion will be null, although the relation of these objects are defined inside the database.. any advice?

FirstOrDefault could be null in any case. It doesn't related to this particular case. So, before using any property, it should be checked whether it is null or not.
var object = list.FirstOrDefault(x=>x.p1='aa');
if(object != null)
{
//use object.Answer
}

Related

Can't access to foreign key properties using Entity Framework in Razor Pages project

I'm trying to display foreign key properties values using class that should have access to it:
#foreach (var item in Model.UserIssues)
{
<div class="card scroll" style="width:22rem;">
<div class="card-header p-4">
<div class="d-flex justify-content-between">
<div>
<p class="card-label mb-0"></p>
<h5>#Html.DisplayFor(i => item.Case.CaseNumber)</h5>
</div>
<div>
<a class="btn button-idle-add" data-bs-toggle="tooltip" data-bs-title="Dodaj Pracownię" asp-route-id="#item.Id" asp-page="/Cases/AddLaboratory"><i class="fa-solid fa-plus"></i></a>
</div>
</div>
<div class="d-flex justify-content-between mt-3">
<div>
<p class="card-input">#Html.DisplayFor(e => item.Case.Principal)</p>
</div>
<div>
<p class="card-input">#Html.DisplayFor(e => item.Case.Date)</p>
</div>
</div>
</div>
<div class="card-body">
<div class="m-1 p-3 card-lab">
<div class="d-flex justify-content-between align-items-center">
<p>#Html.DisplayFor(e => item.IssueNumber)</p>
<p class="pe-3 ps-3" style="border-radius:15px; background-color: palegreen;"></p>
</div>
<p>#Html.DisplayFor(e => item.Specialist.Laboratory)</p>
<p>#Html.DisplayFor(e => item.Specialist.FullName)</p>
</div>
</div>
</div>
}
public class IndexModel : PageModel
{
private readonly IRepository<Issue> issueRepository;
private readonly IRepository<Specialist> specialistRepository;
public IndexModel(IRepository<Issue> issueRepository, IRepository<Specialist> specialistRepository)
{
this.issueRepository = issueRepository;
this.specialistRepository = specialistRepository;
}
public List<Issue> AllIssues { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var loggedUser = specialistRepository.GetAll().FirstOrDefault(u => u.Login == User.Identity.Name);
UserIssues = issueRepository.GetAll().Where(i => i.Specialist.Id == loggedUser.Id).ToList();
return Page();
}
}
So for Specialist it works just fine:
#Html.DisplayFor(e => item.Specialist.FullName)
But for the Case it doesn't work, nothing is displayed:
#Html.DisplayFor(i => item.Case.CaseNumber)
Here are my models for Entity Framework setup:
public class Case
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string CaseNumber { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string Principal { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string Description { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string Date { get; set; }
public ICollection<Issue> Issues { get; set; }
}
public class Issue
{
[Key]
public int Id { get; set; }
public string Comment { get; set; }
[Required(ErrorMessage = "Uzupełnij pole")]
public string IssueNumber { get; set; }
public Case Case { get; set; }
public Specialist Specialist { get; set; }
}
public class Specialist : IdentityUser
{
[Required]
public string Login { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Laboratory { get; set; }
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
public ICollection<Issue> Issues { get; set; }
}
Is my Entity Framework setup wrong? How can I display property values for the Case entity?
Using Entity Framework in case you want to get a specialist with id e775704b-5298-4173-82c8-c15d884e0695 and include the issues for this specialist you can use the include method in order to get the associated data.
Specialist? specialist = context.Specialists.Where(m => m.Id == "e775704b-5298-4173-82c8-c15d884e0695").Include(m => m.Issues).FirstOrDefault();
if (specialist != null)
{
foreach (var issue in specialist.Issues)
{
string issueNumber = issue.IssueNumber;
}
}
You can do the same thing for the entire list like this.
List<Specialist> specialists = context.Specialists.Include(m => m.Issues).ToList();
foreach (var specialist in specialists)
{
foreach (var issue in specialist.Issues)
{
string issueNumber = issue.IssueNumber;
}
}

Retrieve DateTime doesn't Load EF Core

I have the following model.
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public DateTime RegisteredDate { get; set; }
}
From the model, I generate scaffold controller. Then I have the following code:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var obj = await _db.Customers.FindAsync(id);
if (obj == null)
{
return NotFound();
}
return View(obj);
}
It will triggered when user clicked the edit link on the table. But, the RegisteredDate didn't retrieved to the view. I have the following view:
<input asp-for="CustomerId" hidden />
<input asp-for="CustomerName" />
<input asp-for="RegisteredDate" />
Why the RegisteredDate didn't load to the view? But If I add type="datetime", the data is loaded. The problem is, DateTime picker dropdown is not appears.
1st pic is without type="datetime" and 2nd pic is with type="datetime" (notice there's no datetime picker icon on it).
please advice.
thank you.
in your customer class add Datatype to RegisteredDate like this
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string CustomerName { get; set; }
[DataType(DataType.Date)]
public DateTime RegisteredDate { get; set; }
}

selectList razor tag helpers asp.netCore

I want to make a drop down list of "Trailers" and "Customers" available in my "Order" form. I am able to use the Html tag helper to pass Trailer data from database to the view in the "Order" form but i am not able to do the same for Customers using the razor select tag helper. Why isn't the razor select tag helper not passing values from the database to the view? Below are snippets of my code. I am confused as to why it's not working
Trailer Class
public class Trailer
{
public string SerialNumber { get; set; }
public string TrailerNumber { get; set; }
public string TrailerStatus { get; set; }
public int TrailerID { get; set; }
public virtual Order OrderforTrailer { get; set; }
public Trailer()
{
TrailerStatus = "Available";
}
}
Customer class
public class Customer
{
public string CustomerName { get; set; }
public string StreetNumber { get; set; }
public string StreetName { get; set; }
public string ZipCode { get; set; }
public string State { get; set; }
public int CustomerID { get; set; }
public IList<Order> CustomerOrders { get; set; }
}
Order Class
public class Order
{
public string OrderNumber { get; set; }
public string OrderStatus { get; set; }
public int OrderID { get; set; }
public int TrailerForLoadID { get; set; }
public virtual Trailer TrailerForLoad { get; set; }
public int CustomerOrdersID { get; set;}
public virtual Customer CustomerOrders { get; set; }
public Order()
{
OrderStatus = "Available";
}
}
AddOrderViewModel
public string OrderNumber { get; set; }
public int TrailerID { get; set; }
public List<SelectListItem> TrailersForLoad { get; set; }
public int CustomerID { get; set; }
public List<SelectListItem> CustomersOrder { get; set; }
public AddOrderViewModel()
{
}
public AddOrderViewModel(IEnumerable<Trailer> trailersForLoad, IEnumerable<Customer> customersOrder)
{
TrailersForLoad = new List<SelectListItem>();
foreach (var trailer in trailersForLoad)
{
TrailersForLoad.Add(new SelectListItem
{
Value = (trailer.TrailerID).ToString(),
Text = trailer.TrailerNumber
});
};
CustomersOrder = new List<SelectListItem>();
foreach (var customer in customersOrder)
{
CustomersOrder.Add(new SelectListItem
{
Value = (customer.CustomerID).ToString(),
Text = customer.CustomerName
});
};
}
}
Order controller
public IActionResult Add()
{
IList<Trailer> trailerForLoad = context.Trailers.Where
(c => c.TrailerStatus == "Available").ToList();
IList<Customer> customerOrder = context.Customers.ToList();
AddOrderViewModel addOrderViewModel =
new AddOrderViewModel(trailerForLoad, customerOrder);
return View(addOrderViewModel);
}
[HttpPost]
public IActionResult Add(AddOrderViewModel addOrderViewModel)
{
if (ModelState.IsValid)
{
Order newOrder = new Order()
{
OrderNumber = addOrderViewModel.OrderNumber,
TrailerForLoad = context.Trailers.
Where(x => x.TrailerID == addOrderViewModel
.TrailerID).Single(),
CustomerOrders = context.Customers
.Single(x => x.CustomerID==addOrderViewModel.CustomerID)
};
context.Orders.Add(newOrder);
trailerSelected = context.Trailers.Where(x =>
x.TrailerID == addOrderViewModel.TrailerID).Single();
trailerSelected.TrailerStatus = "Unavailable";
context.SaveChanges();
return Redirect("/Order");
}
return View(addOrderViewModel);
}
The form in the view should display a list of customers
<form asp-controller="Order" asp-action="Add" method="post">
<fieldset>
<div class="form-group">
<label asp-for="OrderNumber">Order number </label>
<input class="form-control" asp-for="OrderNumber" />
<span asp-validation-for="OrderNumber"></span>
</div>
<div class="form-group">
<label asp-for="TrailersForLoad">Trailer</label>
#Html.DropDownListFor(x => x.TrailerID, Model.TrailersForLoad)
<span asp-validation-for="TrailersForLoad"></span>
</div>
<div class="form-group">
<label asp-for="CustomerID">Customers Name</label>
<select asp-for="CustomerID"
asp-items="Model.CustomersOrder"></select>
<span asp-validation-for="CustomerID"></span>
</div>
<div>
<input type="submit" value="Submit" name="submitButton" />
</div>
</fieldset>
You are using the SELECT tag helper incorrectly. In your current code, you are using a self closing tag approach! Instead you should use an explicit </SELECT> closing tag
This should work
<select asp-for="CustomerID" asp-items="Model.CustomersOrder"></select>

How to save an image to Database using MVC 4

So I have a project which is a Shopping Cart, I have to save images to the database instead of uploading them to the server, here is my model
namespace ShoppingCart.Models
{
[Bind(Exclude = "ItemID")]
public class Item
{
[ScaffoldColumn(false)]
public int ItemID { get; set; }
[DisplayName("Category")]
public int CategoryID { get; set; }
[DisplayName("Brand")]
public int BrandID { get; set; }
[Required(ErrorMessage = "A Name is required")]
[StringLength(160)]
public string Title { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00,
ErrorMessage = "Price must be between 0.01 and 500.00")]
public decimal Price { get; set; }
[DisplayName("Album Art URL")]
[StringLength(1024)]
public string ItemArtUrl { get; set; }
public byte[] Picture { get; set; }
public virtual Category Category { get; set; }
public virtual Brand Brand { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
}
So Im unsure how to go about the controller to insert images or the view to display them, I have search for information about this but I cant really find anything, Im using entity framework code first.
There are two easy ways to do images -- one is to simply return the image itself in the controller:
[HttpGet]
[AllowAnonymous]
public ActionResult ViewImage(int id)
{
var item = _shoppingCartRepository.GetItem(id);
byte[] buffer = item.Picture;
return File(buffer, "image/jpg", string.Format("{0}.jpg", id));
}
And the view would just reference it:
<img src="Home/ViewImage/10" />
Additionally, you can include it in the ViewModel:
viewModel.ImageToShow = Convert.ToBase64String(item.Picture);
and in the view:
#Html.Raw("<img src=\"data:image/jpeg;base64," + viewModel.ImageToShow + "\" />");
For the data-store, you would simply use a byte array (varbinary(max)) or blob or any compatible type.
Uploading images
Here, an object called HeaderImage is an EntityFramework EntityObject. The controller would look something like:
[HttpPost]
public ActionResult UploadImages(HttpPostedFileBase[] uploadImages)
{
if (uploadImages.Count() <= 1)
{
return RedirectToAction("BrowseImages");
}
foreach (var image in uploadImages)
{
if (image.ContentLength > 0)
{
byte[] imageData = null;
using (var binaryReader = new BinaryReader(image.InputStream))
{
imageData = binaryReader.ReadBytes(image.ContentLength);
}
var headerImage = new HeaderImage
{
ImageData = imageData,
ImageName = image.FileName,
IsActive = true
};
imageRepository.AddHeaderImage(headerImage);
}
}
return RedirectToAction("BrowseImages");
}
The View would look something like:
#using (Html.BeginForm("UploadImages", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="row">
<span class="span4">
<input type="file" name="uploadImages" multiple="multiple" class="input-files"/>
</span>
<span class="span2">
<input type="submit" name="button" value="Upload" class="btn btn-upload" />
</span>
</div>
}

DbUpdateConcurrencyException not raised Database First

I am currently following this tutorial for Concurrency Handling.
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application
However, the DbUpdateConcurrencyException is not raised in my case. I'm using Database first approach in EF 5.0
I have Carrier object with 0..1 relationship with Address & Contact objects.
I have created the a RowVersion field in the Carrier table .
My code is as follows:
public Carrier UpdateCarrier(Carrier carrier)
{
try
{
var entry = ariesConfigContext.Entry<Carrier>(carrier);
if(entry.State == System.Data.EntityState.Detached)
{
var attachedEntity = ariesConfigContext.Carriers.Include(con => con.Contact).Include(a => a.Address).Single(c => c.CarrierID == carrier.CarrierID);
if(attachedEntity != null)
{
var attachedEntry = ariesConfigContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(carrier);
attachedEntity.Contact = carrier.Contact;
attachedEntity.Address = carrier.Address;
}
else
{
entry.State = System.Data.EntityState.Modified;
}
}
ariesConfigContext.SaveChanges();
}
catch(DbUpdateConcurrencyException ex)
{
//do something
}
return carrier;
}
My Model is as follows:
namespace Aries.Domain.Models.AriesConfigDB
{
using System;
using System.Collections.Generic;
public partial class Carrier
{
public Carrier()
{
this.UserProfiles = new HashSet<UserProfile>();
}
public int CarrierID { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Memo { get; set; }
public string DBServerName { get; set; }
public string DBName { get; set; }
public string DBUser { get; set; }
public string DBPassword { get; set; }
public Nullable<int> AddressID { get; set; }
public Nullable<int> ContactID { get; set; }
public bool IsDeleted { get; set; }
public byte[] RowVersion { get; set; }
public virtual Address Address { get; set; }
public virtual Contact Contact { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
}
My View is:
#model Aries.Domain.Models.AriesConfigDB.Carrier
<div>
<div class="message-error">
#if (TempData["ErrorMessage"] != null)
{
<p>#TempData["ErrorMessage"]</p>
}
</div>
#Html.HiddenFor(model => model.CarrierID)
#Html.HiddenFor(model => model.RowVersion)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Abbreviation)
</div>
....
....
I'm unable to get the Exception to be raised. Any help is appreciated.
UPDATE:
I already have a Metadata class for Carrier as below and I've added the [Timestamp] attribute there.
public class CarrierMetadata
{
public int CarrierID { get; set; }
[Required]
[MaxLength(255, ErrorMessage = "Carrier Name must be 255 characters or less")]
[Display(Name = "Carrier Name")]
public string Name { get; set; }
....
....
[Required]
[ConcurrencyCheck]
[Timestamp]
public byte[] RowVersion { get; set; }
}
And my Carrier class has the following attribute:
[MetadataType(typeof(CarrierMetadata))]
public partial class Carrier
{
}
But it still doesn't work.