ViewDataDictionary Model Type Exception - c#

I am running an App with two main tables - TJob & TJobHistory. The TJobHistory table serves as an audit table to any changes made in the TJob table. I am trying to change my 'details' view so it will display the TJobHistory of the specific TJob entry that the user clicks on in the homepage. I am running into the following exception when I try to load the details page for any TJobs entry:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'Planner_App.Models.TJob', but this ViewDataDictionary instance requires a model item of type 'Planner_App.Models.TJobHistory'.
Controller Code
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
//Foreign key pulls//
var TJob = await _context.TJob
.Include(t => t.intCustomer)
.Include(t => t.intDeveloper)
.Include(t => t.intJobStatus)
.Include(t => t.intJobType)
.FirstOrDefaultAsync(m => m.intJobId == id);
if (TJob == null)
{
return NotFound();
}
return View(TJob);
}
View Code
#model Planner_App.Models.TJobHistory
#{
ViewData["Title"] = "Details";
}
I am still very new to MVC so I apologize if this isn't helpful - but I can't seem to figure out what I'm doing wrong here. If anyone could point me in the right direction it would be much appreciated!

you have to fix you view model
#model Planner_App.Models.TJob
or if you really need to use TJobHistory and it is possible to convert TJob to TJobHistory you can try something like this
var model = await _context.TJob
.Include(t => t.intCustomer)
.Include(t => t.intDeveloper)
.Include(t => t.intJobStatus)
.Include(t => t.intJobType)
.Where(m => m.intJobId == id)
Select(i=> new TJobHistory
{
Id=i.Id,
... TJobHistory properties
}).FirstOrDefaultAsync();
if model == null)
{
return NotFound();
}
return View(model);
or if TJobHistory is a copy of TJob maybe if would be enough just replace _context.TJob with _context.TJobHistory
var tJobHistory = await _context.TJobHistory
....

Related

The name 'Ok' does not exist in the current context, Razor Code behind

In my code behind I have the following and I'm getting the error 'Ok' does not exist in current context. I'm trying to populate a form with textboxes.
public async Task<IActionResult> OnGetDetails(string custName)
{
var x = _context.Customer
.Join(_context.Sales,
x => x.CustId,
c => c.CustId,
(x,c) => new
{
customerName = x.Name,
address = x.Address,
sale = x.SaleDate
}).ToListArraySync();
return Ok(await q.ToListArraySync()); //causes error 'Ok' doesn't exist in current context
}
I need to prefill the form on the page out with the data. I'm able to get the data on the default OnGet(), however, I need to join two tables in this handler
You can "manually" return OkObjectResult or JsonResult. ControllerBase.Ok is just a convenience method that actually returns an OkObjectResult
public async Task<IActionResult> OnGetDetails(string custName)
{
// ...
return new OkObjectResult(await q.ToListArraySync());
// or
return new JsonResult(await q.ToListArraySync());
}
I got it working.
I added a FK to the table and rebuilt the models and then this:
public async Task<IActionResult> OnGetDetails(string custName)
{
var x = _context.Customer
.Join(_context.Sales,
x => x.CustId,
c => c.CustId,
(x,c) => new
{
customerName = x.Name,
address = x.Address,
sale = x.SaleDate
}).ToListArraySync();
return Ok(await q.ToListArraySync());
}
became this:
public async Task<IActionResult> OnGetDetails(string custName)
{
var x = _context.Customer
.Where(c => c.CustomerName == custName)
.Include(x => x.Sales).ToListArraySync()
}
and now I can see the data from both tables on my view

C#, ASP.NET MVC, Entity Framework : user.cart items are null

I am using ASP.NET MVC with Entity Framework. I have a class that handles SQL called DataLayer.
There is a User object in the DataLayer.
I built a function that includes all the cart's items and returns the user by id, then makes the user in the datalayer as the found user (so it could log in).
The function code is:
public User userWithInclude(int? userId)
{
List<User> users = Users.Include(x => x.Cart)
.Include(x => x.Cart.shakes)
.Include(x => x.Cart.equipmentList)
.Include(x => x.orders)
.Include(m => m.menus).ToList();
User user = users.ToList().Find(x => x.ID == userId);
return user;
}
Here's a snippet of the function the logs in :
public IActionResult LogInUser(User vm)
{
User user = DataLayer.Data.Users.ToList()
.Find(x => x.userName == vm.userName && x.password == vm.password);
if (user != null)
{
User user2 = DataLayer.Data.userWithInclude(user.ID);
DataLayer.User = user2;
if (user2.Cart == null)
{
user2.Cart = new Cart();
}
DataLayer.Data.SaveChanges();
return RedirectToAction("Index");
}
}
When I debug before the last line of the log in when I hover over the user object the cart's items all the vars exist but the object inside is null.
In the SQL Server database, the data exists with all the details.
How could it be?
You can rewrite the get user class like this.
public User GetUserWithInclude(int? userId)
{
return DataLayer.Data.Users.Include(x => x.Cart)
.Include(x => x.Cart.shakes)
.Include(x => x.Cart.equipmentList)
.Include(x => x.orders)
.Include(m => m.menus)
.FirstOrDefault(x => x.ID == userId);
}
Then your login actionresult can look like this
public IActionResult LogInUser(User vm)
{
if(Login(vm.userName, vm.password)){
return RedirectToAction("Index");
}
// handle login failure
}
Make a seperate loginMethod
public bool Login(string userName, string password)
{
User user = DataLayer.Data.Users.FirstOrDefault(x => x.userName == userName && x.password == password);
if (user != null)
{
User user2 = DataLayer.Data.GetUserWithInclude(user.ID);
DataLayer.User = user2;
if (user2.Cart == null)
{
user2.Cart = new Cart();
}
DataLayer.Data.SaveChanges();
return true;
}
return false;
}
If this doesn't work you can try and get the cart after the user and debug if that's null, but I don't know enough about your foreign keys and relations etc to help you with that
I think you must use ThenInclude() method. You can get more detailed information from from here
For example:
public User GetUserWithInclude(int? userId)
{
return DataLayer.Data.Users.Include(x => x.Cart)
.ThenInclude(m=>m.shakes)
.Include(x=>x.Cart)
.ThenInclude(m => m.equipmentlist)
.Include(m=>m.Orders)
.Include(m => m.menus)
.FirstOrDefault(x => x.ID == userId);
}

Projecting included objects

Im looking for a method to project included objects in EntityFramework Core in a way we can project objects (Select function):
[HttpGet]
public async Task<IActionResult> GetBooks()
{
return Json(new { data = await _db.Books.Select(b => _mapper.Map<BookDto>(b)).ToListAsync() });
}
Here is my code where I tried to project included objects but its not valid:
[HttpGet]
public async Task<JsonResult> GetMessagesAsync()
{
var msgs = await _db.Messages.OrderBy(m => m.Sent).Include(m => _mapper.Map<AppUserDto>(m.AppUser)).ToListAsync();
return Json(new { data = msgs });
}
EDIT1:
I want to project included AppUser objects into AppUserDto.
EDIT2:
public MappingProfile()
{
CreateMap<Book, BookDto>();
CreateMap<BookDto, Book>();
CreateMap<Client, ClientDto>();
CreateMap<ClientDto, Client>();
CreateMap<Reservation, ReservationDto>();
CreateMap<ReservationDto, Reservation>();
CreateMap<AppUser, AppUserDto>();
CreateMap<AppUserDto, AppUser>();
}
If you have your mapper set up correctly you should be able to just do the following:
var msgs = await _db.Messages
.OrderBy(m => m.Sent)
.Include(m => m.AppUser)
.Select(m => _mapper.Map<MessageDto>(m))
.ToListAsync();
So your MessageDto will need to have AppUserDto AppUser property among others.

Updating multiple records with context, ASP.NET C#

I want to find everything with a certain matching variable, and then update that variable.
Updating that exact variable for every record that was found.
I know context is used to query the database.
How could I do this?
Its not working:
public async Task<IActionResult> Checkout()
{
IEnumerable<Orders> orders = _ocontext.GetAllOrders();
var movingcart = orders.Where(p => p.Cart == 1);
foreach (var item in movingcart)
{
item.Cart++;
}
_context.Update(movingcart);
await _context.SaveChangesAsync();
return View();
}
EDIT::::
I FIGURED IT OUT!:
public async Task<IActionResult> Checkout()
{
var Temp = (from d in _context.Orders
where d.Cart == 1
select d).Single();
Temp.Cart++;
_context.Update(Temp);
await _context.SaveChangesAsync();
return View();
}
I missed this part where you said "I want to find everything with a certain matching variable, and then update that variable."
To do that, you could do this:
var cartItems = _context.Orders.Where(x => x.Cart == 1).ToList();
foreach (var item in cartItems)
{
item.Cart++;
_context.Update(item);
await _context.SaveChangesAsync();
}
public async Task<IActionResult> Checkout()
{
var temp= _context.Orders.FirstOrDefault(x => x.Cart == 1);
temp.Cart++;
_context.Update(temp);
await _context.SaveChangesAsync();
return View();
}

Partially update a complex object in Entity Framework Core in a web api

I'm having issues trying to update a rather complex object in an ASP.NET Core Web API.
I changed my put method to the following code:
[Authorize(Policy = "IsAuthor")]
[HttpPut("{id}")]
public async Task<IActionResult> PutCIApplication([FromRoute] Guid id, [FromBody] CIApplication cIApplication)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != cIApplication.ID)
{
return BadRequest();
}
string userLang = HttpContext.Request.Headers["UserCulture"].ToString();
cIApplication.Translations[userLang].Name = cIApplication.Name;
cIApplication.Translations[userLang].Description = cIApplication.Description;
var dbCIApplication = await _context.CIApplications.Include(c => c.Translations)
.Include(c => c.DeploymentScenarios).ThenInclude(d => d.InstallSequence).ThenInclude(s => s.Steps).ThenInclude(s => s.SubSteps)
.Include(c => c.DeploymentScenarios).ThenInclude(d => d.UninstallSequence).ThenInclude(s => s.Steps).ThenInclude(s => s.SubSteps)
.Include(c => c.SoftwareMeteringRules).Include(c => c.Catalogs).Include(c => c.Categories)
.AsNoTracking()
.SingleOrDefaultAsync(m => m.ID == id);
HashSet<string> excludedProperties = new HashSet<string>();
excludedProperties.Add("OwnerCompanyID");
excludedProperties.Add("OwnerCompany");
excludedProperties.Add("CreatedDate");
excludedProperties.Add("CreatedBy");
excludedProperties.Add("UpdatedDate");
excludedProperties.Add("UpdatedBy");
var patch = JsonPatchCreation.CreatePatch(dbCIApplication, cIApplication, excludedProperties);
if ((await _authorizationService.AuthorizeAsync(User, cIApplication, "CIAuthoringManagement")).Succeeded)
{
patch.ApplyTo(dbCIApplication);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CIApplicationExists(id))
{
return NotFound();
}
else
{
throw;
}
}
catch (Exception)
{
throw;
}
return NoContent();
}
else
{
return Unauthorized();
}
}
As you can see, the CIApplication class contains lots of relationships with other classes and dependent themselves have a lot of relations.
This controller action works fine with a simple object which doesn't have any relations.
But in this case, the controller doesn't throw any exception, but the object isn't updated in database.
I first tried without the .AsNoTracking, but in this case I have an exception saying that a DeploymentScenarios is already tracked.
I want to do a partial update because some relations aren't sent to my native client by the get action (in this case OwnerCompany) and so when the object is sent for update the navigation property have a null value and thus EF Core removes the relation.
If someone have an idea on how to make it work in the most simplest way it would be great !
Thanks !

Categories

Resources