High Entity Framework Core execution times - asp.net-core

I've built a simple Asp.Net Core MVC app for use at work displaying data from a view in our MSSQL database. When querying this view from SSMS, execution time is ~100ms on average. When the same query is executed within my app, execution time is anywhere from ~800ms to ~1.5s.
Here is the LINQ from the controller:
public IActionResult Index()
{
var query =
from p in _context.vWebQuery
where p.Almachine == "600L"
orderby p.Aldatsta
select p;
return View(query);
}
Here is the Entity Class:
namespace BetaKestrel2.Models
{
public class vWebQuery
{
public double Wruntim { get; set; }
public short Wper { get; set; }
public double Quantity { get; set; }
[Column("Total Op TIme")]
public double? TotalTime { get; set; }
public string Alwon { get; set; }
public short Alopnum { get; set; }
public string Almachine { get; set; }
public double Alpersta { get; set; }
[DisplayFormat(DataFormatString = "{0:F2}")]
public double Allen { get; set; }
public short Alprevop { get; set; }
[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime Aldatsta { get; set; }
public string Altimsta { get; set; }
public string Alstatus { get; set; }
public short Alperno { get; set; }
public string Macid { get; set; }
public string Macdesc { get; set; }
public string Partid { get; set; }
public string Partrevisionid { get; set; }
public string Routingmethod { get; set; }
public double Wqleft { get; set; }
public string Wstate { get; set; }
public string Wdesc { get; set; }
public string Partdesc { get; set; }
public string Toolid { get; set; }
public string Childpartid { get; set; }
public string msection { get; set; }
}
}
And the DbContext:(using .Net Core 3.0 for the .HasNoKey())
public partial class EfacDBContext : DbContext
{
public EfacDBContext()
{
}
public EfacDBContext(DbContextOptions<EfacDBContext> options)
: base(options)
{
}
public DbSet<vWebQuery> vWebQuery { get; set; }
public DbSet<vGRN> vGRN { get; set; }
public DbSet<vQuotationTracker> vQuotationTracker { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<vWebQuery>(entity =>
{
entity.HasNoKey();
entity.ToTable("vwebquery");
});
And an example .cshtml View:
#model IEnumerable<BetaKestrel2.Models.vWebQuery>
#{
ViewData["Title"] = "600L";
string highlight = "";
}
<h1>Work Centre Plan - #ViewData["Title"]</h1>
<table class="table table-sm table-bordered">
<thead>
<tr>
<th>Works Order</th>
<th>Part Number</th>
<th>Description</th>
<th>Op Number</th>
<th>Quantity</th>
<th>Latest Start Date</th>
<th>Previous Op</th>
<th>Total Op Time (mins)</th>
<th>Status</th>
<th>Qty Left</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
#if (item.Wstate == "COMP")
{
highlight = "background-color: green;";
}
else if (item.Alprevop == 0)
{
highlight = "background-color: yellow;";
}
else
{
highlight = "";
}
<tr style="#highlight">
<td>#Html.DisplayFor(modelItem => item.Alwon)</td>
<td>#Html.DisplayFor(modelItem => item.Partid)</td>
<td>#Html.DisplayFor(modelItem => item.Partdesc)</td>
<td>#Html.DisplayFor(modelItem => item.Alopnum)</td>
<td>#Html.DisplayFor(modelItem => item.Quantity)</td>
<td>#Html.DisplayFor(modelItem => item.Aldatsta)</td>
<td>#Html.DisplayFor(modelItem => item.Alprevop)</td>
<td>#Html.DisplayFor(modelItem => item.TotalTime)</td>
<td>#Html.DisplayFor(modelItem => item.Wstate)</td>
<td>#Html.DisplayFor(modelItem => item.Wqleft)</td>
</tr>
}
</tbody>
</table>
EF Core converts to the following SQL
SELECT
[v].[Aldatsta]
,[v].[Allen]
,[v].[Almachine]
,[v].[Alopnum]
,[v].[Alperno]
,[v].[Alpersta]
,[v].[Alprevop]
,[v].[Alstatus]
,[v].[Altimsta]
,[v].[Alwon]
,[v].[Childpartid]
,[v].[Macdesc]
,[v].[Macid]
,[v].[Partdesc]
,[v].[Partid]
,[v].[Partrevisionid]
,[v].[Quantity]
,[v].[Routingmethod]
,[v].[Toolid]
,[v].[Total Op TIme]
,[v].[Wdesc]
,[v].[Wper]
,[v].[Wqleft]
,[v].[Wruntim]
,[v].[Wstate]
,[v].[msection]
FROM [vwebquery] AS [v]
WHERE
[v].[Almachine] = N'BENDD'
AND [v].[Almachine] IS NOT NULL
ORDER BY
[v].[Aldatsta]
Results in:
info: Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor[4]
Executed ViewResult - view Index executed in 1540.6805000000002ms.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action BetaKestrel2.Controllers.BenddController.Index (BetaKestrel2) in 1541.4348ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'BetaKestrel2.Controllers.BenddController.Index (BetaKestrel2)'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 1541.8467ms 200 text/html; charset=utf-8
Whereas, in SSMS
Total execution time 124ms
I tried the AsNoTracking() and that didn't make a difference. Your last comment got me curious. 291 rows are returned from the query so I tried .Take(5) and execution time went down to 24ms. Could it literally just be the iteration of my foreach loop in the view that is taking up all the time?

When querying this view from SSMS, execution time is ~100ms on
average. When the same query is executed within my app, execution time
is anywhere from ~800ms to ~1.5s.
SQL Server Management Studio (SSMS)
RAW SQL NO OVER HEAD
EF Entityfamework
put this
var query =
from p in _context.vWebQuery
where p.Almachine == "BENDD"
orderby p.Aldatsta
select p;
in a block which times it, loop it 3 times and take the last time.
--create dbContext here. (_context)
--start loop (run 3 times)
--start timer
var query = (
from p in _context.vWebQuery
where p.Almachine == "BENDD"
orderby p.Aldatsta
select p
).Tolist();
--end timer -this is what you want to compare after the 3 run.
- yes it will be slower but you could make as non tracking
- should be a must fairer comparison.
-- end loop
tip - put this in debug, put break-point on index... change your query to ToList(), so that it executes the query at that point and not wen its in the view section.
Code from test: Modified
for (int i = 0; i <= 3; i++)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var query = (from p in _context.vWebQuery
where p.Almachine == "600L"
orderby p.Aldatsta
select p
).ToList();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
}
.....

Related

API - Blazor server - foreign key ICollection is always null - EF core

I'm new to API and Blazor and I'm trying to follow this example (https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/crud?view=aspnetcore-5.0)
Below you can see my models and code.
Model:
public class Student {
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime EnrollmentDate { get; set; }
[JsonIgnore]
public virtual ICollection<Enrollment> Enrollments { get; set;}
}
API controller:
[HttpGet]
[Route("{id:int}")]
public async Task<ActionResult<Student>> GetStudent(int id)
{
try
{
var result = await context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.StudentId == id);
//var result = await context.Students
// .Where(s => s.StudentId == id)
// .Select(s => new
// {
// Student = s,
// Enrollment = s.Enrollments
// })
// .FirstOrDefaultAsync();
if (result == null)
{
return NotFound();
}
return Ok(result);
}
catch (Exception)
{
return StatusCode(StatusCodes.Status500InternalServerError, "Error receiving data from database");
}
}
Student model in Blazor server
public class Student
{
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime EnrollmentDate { get; set; }
[JsonIgnore]
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
My Blazor Student base:
public class StudentDetailsBase : ComponentBase
{
[Inject]
public IEnrollmentService EnrollmentService { get; set; }
[Inject]
public IStudentService StudentService { get; set; }
public Student Student { get; set; }
public List<Enrollment> Enrollments { get; set; }
//public ICollection<Student> Student { get; set; }
[Parameter]
public string Id { get; set; }
protected override async Task OnInitializedAsync()
{
Id = Id ?? "1";
Student = await StudentService.GetStudent(int.Parse(Id));
Enrollments = (await EnrollmentService.GetEnrollmentBySID(int.Parse(Id))).ToList();
//Student = (await StudentService.GetStudent(int.Parse(Id))).ToList();
}
}
And my Student display page:
#if (Student == null)
{
<p>Loading ...</p>
}
else
{
<div>
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Enrollment Date</th>
<th>All enrollments</th>
</tr>
</thead>
<tbody>
<tr>
<td>#Student.FirstName</td>
<td>#Student.LastName</td>
<td>#Student.EnrollmentDate</td>
#foreach (var i in Student.Enrollments)
{
<td>#i.Course.Title</td>
<td>#i.Grade</td>
}
</tr>
</tbody>
</table>
</div>
}
I've tried to google the problem and i can not figure out what i'm doing wrong. My Student.Enrollments in always null. Which causes my Blazor server to throw an error.
When i test my API with Postman it's working fine.
Hopefully someone will point me in the right direction on how to solve this.
Thank you.
Kind regards.

How to deal with this decimal error for a price?

I'm working on this app that should show on "localhost/catalog" some data. I have a library for the models and for the services that the application might use. I am getting this error:
InvalidOperationException: The property 'Price' is not a navigation
property of entity type 'StoreAsset'. The 'Include(string)' method can
only be used with a '.' separated list of navigation property names.Microsoft.EntityFrameworkCore.Query.Internal.IncludeCompiler.WalkNavigations(IEntityType entityType, IReadOnlyList<string> navigationPropertyPaths, IncludeLoadTree includeLoadTree, bool shouldThrow)
Here is the code that I'm using (controller, models and view) and the service methods on bottom:
public class CatalogController : Controller
{
private IStoreAsset _assets;
public CatalogController(IStoreAsset assets)
{
_assets = assets;
}
public ActionResult Index()
{
var assetModels = _assets.GetAll();
var listingResult = assetModels
.Select(result => new AssetIndexListingModel
{
Id = result.Id,
Tipology = _assets.GetTipology(result.Id),
Size = _assets.GetSize(result.Id),
Price = decimal.Parse(_assets.GetPrice(result.Id))
});
var model = new AssetIndexModel()
{
Assets = listingResult
};
return View(model);
}
public class AssetIndexListingModel
{
public int Id { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
public string Tipology { get; set; }
public string ImageUrl { get; set; }
}
public abstract class StoreAsset
{
public int Id { get; set; }
[Required]
public Status Status { get; set; }
[Required]
public decimal Price { get; set; }
public string ImageUrl { get; set; }
}
public class Dress : StoreAsset
{
[Required]
public string Color { get; set; }
[Required]
public string Tipology { get; set; }
[Required]
public string Size { get; set; }
}
#model Models.Catalog.AssetIndexModel
<div id="assets">
<h3></h3>
<div id="assetsTable">
<table class="table table-condensed" id="catalogIndexTable">
<thead>
<tr>
<th>Size</th>
<th>Price</th>
<th>Tipology</th>
</tr>
</thead>
<tbody>
#foreach (var asset in Model.Assets)
{
<tr class="assetRow">
<td class="">
<a asp-controller="Catalog" asp-action="Detail" asp-route-id="#asset.Id">
<img src="#asset.ImageUrl" class="imageCell" />
</a>
</td>
<td class="">#asset.Price</td>
<td class="">#asset.Size</td>
<td class="">#asset.Tipology</td>
</tr>
}
</tbody>
</table>
</div>
public class StoreAssetService : IStoreAsset
{
private Context _context;
public StoreAssetService(Context context)
{
_context = context;
}
public void Add(StoreAsset newAsset)
{
_context.Add(newAsset);
_context.SaveChanges();
}
public IEnumerable<StoreAsset> GetAll()
{
return _context.StoreAssets
.Include(asset => asset.Status)
.Include(asset => asset.Price);
}
public StoreAsset GetById(int id)
{
// Return a query (same as returning GetAll().FirstOrDefault(...))
return _context.StoreAssets
.Include(assets => assets.Status)
.Include(assets => assets.Price)
// So it can return null with no problem
.FirstOrDefault(asset => asset.Id == id);
}
public StoreBranch GetCurrentLocation(int id)
{
throw new NotImplementedException();
}
// To implement and test
public string GetPrice(int id)
{
return _context.Dresses.FirstOrDefault(p => p.Id == id).Price.ToString();
}
public string GetSize(int id)
{
return _context.Dresses.FirstOrDefault(s => s.Id == id).Size;
}
public string GetStatus(int id)
{
throw new NotImplementedException();
}
public string GetTipology(int id)
{
var dress = _context.StoreAssets.OfType<Dress>()
.Where(b => b.Id == id);
// For now return other if it's not a party dress
return dress.Any() ? "Party" : "Other";
}
}
Should I use some ForeignKey attribute or change Price to a string?
Any help would be great thanks
As pointed out in the error message, the Include is for the Navigation property only.
You need to change below:
return _context.StoreAssets
.Include(asset => asset.Status)
.Include(asset => asset.Price);
To:
return _context.StoreAssets
.Include(asset => asset.Status).ToList();
Reference: https://learn.microsoft.com/en-us/ef/core/modeling/relationships#definition-of-terms
https://learn.microsoft.com/en-us/ef/core/querying/related-data
I am having yet another problem. When I go to "localhost/catalog" the page should display all columns/entries that I have in the database but it only displays one column. Is there something wrong in the foreach cicle?

search function in ASP.NET MVC not working properly

i have a student table in my database that i created and i have a view that displays a list of all the students grouped by class... on top of the view i made a textbox and a search button to be able to access the student information faster. The problem is that i when i enter the first name and the last name in the textbox, nothing comes up. When i enter only the first name or only the last name, then it finds it. I'm new to programming and i can't figure out how to make it work. I would really appreciate if someone can help me with this. This is part of my code:
[HttpGet]
public ActionResult ViewStudents()
{
ViewBag.classes = db.Courses.ToList();
var studentCourses = db.StudentCourses.OrderBy(s=>s.Person.FirstName).ToList();
return View(studentCourses);
}
[HttpPost]
public ActionResult ViewStudents(string SearchString)
{
var student=new List<int>();
List<StudentCourse>sc=new List<StudentCourse>();
ViewBag.classes = db.Courses.ToList();
var studentCourse=db.StudentCourses.ToList();
var studentCourses = db.StudentCourses.OrderBy(s => s.Person.FirstName).ToList();
var substring = SearchString.IndexOf(" ").ToString();
if (!string.IsNullOrEmpty(SearchString))
{
student = (from p in db.People
where (p.FirstName.Contains(SearchString)) && (p.LastName.Contains(substring))||((p.FirstName.Contains(SearchString)) || (p.LastName.Contains(SearchString)))
select p.PersonId).ToList();
}
foreach (var s in studentCourse)
{
foreach (var i in student)
{
if (s.StudentId == i)
{
sc.Add(s);
}
}
}
return View(sc);
}
This is my view:
#model List<SchoolFinalProject.Models.StudentCourse>
#using (Html.BeginForm())
{
<div style="font-size:16px;"> <input type="text" id="search" placeholder="search" Name="SearchString" /><span class="glyphicon glyphicon-search"></span>
<input type="submit" value="search"></div>
}
#{
List<int> c = new List<int>();
foreach (var courses in ViewBag.classes)
{
foreach(var s in Model)
{
if(courses.CourseId==s.CourseId)
{
c.Add(courses.CourseId);
}
}
}
}
#foreach (var course in ViewBag.classes)
{
if(c.Contains(course.CourseId))
{
<h2>#course.Name<span>-</span>#course.Gender</h2>
<table class="table table-hover table-bordered table-striped">
<tr><th>First Name</th><th>Last Name</th><th>Email</th><th>Phone Number</th><th>Address</th><th>Date Of Birth</th></tr>
#foreach (var s in Model)
{
if(course.CourseId==s.CourseId)
{
<tr>
<td>#s.Person1.FirstName</td>
<td>#s.Person1.LastName</td>
<td>#s.Person1.Email</td>
<td>#s.Person1.PhoneNumber</td>
<td>#s.Person1.Address</td>
<td>#s.Person1.DateOfBirth</td>
<td>
<span class="glyphicon glyphicon-edit"></span>
#Html.ActionLink("Edit", "Edit","Person", new { id = s.Person1.PersonId }, null) |
<span class="glyphicon glyphicon-trash"></span>
#Html.ActionLink("Details", "Details","Person", new { id = s.Person1.PersonId }, null)
</td>
</tr>
}
}
</table>
}
}
Go to top of page
this is my person Model:
public partial class Person
{
public Person()
{
this.Bonus = new HashSet<Bonu>();
this.ConversationHistories = new HashSet<ConversationHistory>();
this.ConversationHistories1 = new HashSet<ConversationHistory>();
this.EmployeePaymentDetails = new HashSet<EmployeePaymentDetail>();
this.StudentCourses = new HashSet<StudentCourse>();
this.StudentCourses1 = new HashSet<StudentCourse>();
this.TeacherCourses = new HashSet<TeacherCourse>();
this.Reminders = new HashSet<Reminder>();
}
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public PersonType PersonTypeId { get; set; }
public Nullable<System.DateTime> LastModified { get; set; }
public Nullable<int> Gender { get; set; }
public Nullable<int> Status { get; set; }
public string FullName
{
get { return FirstName + ", " + LastName; }
}
public virtual ICollection<Bonu> Bonus { get; set; }
public virtual ICollection<ConversationHistory> ConversationHistories { get; set; }
public virtual ICollection<ConversationHistory> ConversationHistories1 { get; set; }
public virtual ICollection<EmployeePaymentDetail> EmployeePaymentDetails { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
public virtual ICollection<StudentCourse> StudentCourses1 { get; set; }
public virtual ICollection<TeacherCourse> TeacherCourses { get; set; }
public virtual ICollection<Reminder> Reminders { get; set; }
}
}
You might want to try concatenating the first and last name properties in your person model like this:
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
There is a very good tutorial on what you are trying to do here: https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-a-more-complex-data-model-for-an-asp-net-mvc-application
Also see this page of same tutorial: https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
As an aside, you might want to check out using the Datatables plugin, which gives you search functionality without have to query your database with each search: https://datatables.net

How to make this razor view working?

in my MVC web apps, this is the models which uses icollection object,
public class EmpProfile
{
public int EmpProfileID { get; set; }
public string EmpName { get; set; }
public string EmpNum { get; set; }
public string ManagerEditor { get; set; }
public string DocCreatedBy { get; set; }
public virtual ICollection<PerfPlan> PerfPlans { get; set; }
public virtual ICollection<ProgReview> ProgReviews { get; set; }
}
and this is PerfPlan model, the other model ProgReviews is similar like this one.
public class PerfPlan
{
public int ID { get; set; }
public int EmpProfileID { get; set; }
public string EmpName { get; set; }
public string EmpNum { get; set; }
public string Title { get; set; }
....
public virtual EmpProfile EmpProfile { get; set; }
}
Basically, it builds one to many relationship between EmpProfile and PerfPlan, ProgReview. So one EmpProfile has 0 or many Performance plan and Progress Review data (model).Now, in my Index razor of EmpProfile, I want to list all PerfPlan and ProgReview which related to each EmpProfile, I build something like this:
#model IEnumerable<PerfM.Models.EmpProfile>
<table>
#foreach (var item in Model)
{
<tr class="#selectedRow">
<td >
#Html.ActionLink(#item.EmpName, "Index", new { id = item.EmpProfileID })
</td>
</tr>
//here I need to list all PerfPlan and ProgReview related to this EmpProfile and list under this row.
Can any expert help me to continue the codes below?
Thanks a lot,
Just use simple foreach loops like this (inside of your foreach loop) :
foreach(var plan in item.PerfPlans)
{
// here you can access your PerfPlan properties like:
<tr>
<td> #plan.Id </td>
<td> #plan.EmpName</td>
<td> #plan.EmpNum </td>
...
</tr>
}
foreach(var review in item.ProgReviews)
{
...
}
And in your Controller don't forget to include your collections:
var profiles = context.EmpProfiles.Include("PerfPlans").Include("ProgReviews");

MVC ViewModel errors

Goal: To create a re-usable drop down menu that lists my website's administrators, managers and agents. These types of users are defined by the .NET Simplemembership webpages_Roles and webpages_UsersInRoles tables.
So Far:
I have a UserProfile table in my database which has 25 columns. I have a corresponding domain model of the same name which is accessed from my UsersContext() EF.
The drop down menu only needs to list the User's FirstName, LastName and UserId so instead of working with the complete domain model, I created the following ViewModel:
namespace MyModels.Models.ViewModels
{
public class AdminsAndAgentsListVM
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int UserId { get; set; }
}
}
I then added the following to my Account controller (notice I'm not working with partial view yet):
public ActionResult AdminsAndAgentsList()
{
UsersContext _db = new UsersContext(); //provides me access to UserProfiles data
var admins = Roles.GetUsersInRole("Admin"); //gets users with this role
var viewModel = _db.UserProfiles
.Where(x => admins.Contains(x.UserName)); //Selects users who match UserName list
return View(viewModel);
}
I then scaffold a list view and base it on the strongly typed ViewModel:
#model IEnumerable<MyModels.Models.ViewModels.AdminsAndAgentsListVM>
#{
ViewBag.Title = "AdminsAndAgentsList";
}
<h2>AdminsAndAgentsList</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.UserId)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserId)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
I do a successful build and when I run the web page I get the following error:
The model item passed into the dictionary is of type'System.Data.Entity.Infrastructure.DbQuery1[My.Models.UserProfile]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable1[My.Models.ViewModels.AdminsAndAgentsListVM]'.
If I recreate the view but strongly type it agains the UserProfile, it works fine. So how to re work this so I can strongly type against my ViewModel instead? Please provide examples if possible. I am new to C# and MVC and really benefit from the seeing the code first hand. Much appreciate the help!
EDIT -----------------------------
Here is the object for the UserProfile:
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
[Required]
[ReadOnly(true)]
[DisplayName("SubscriberID")]
public int? SubscriberId { get; set; } //Foreign key
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.")]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.")]
[Display(Name = "Last Name")]
public string LastName { get; set; }
//public DateTime DOB { get; set; }
[DataType(DataType.Date)]
public DateTime? DOB { get; set; } //This allows null
public bool? Gender { get; set; }
[Required]
[MaxLength(250)]
[EmailAddress]
public string Email { get; set; }
[MaxLength(250)]
[EmailAddress]
[NotEqualTo("Email", ErrorMessage = "Alt Email and Email cannot be the same.")]
public string AltEmail { get; set; }
[MaxLength(250)]
[EmailAddress]
public string FormEmail { get; set; }
public Address Address { get; set; }
[MaxLength(20)]
public string Telephone { get; set; }
[MaxLength(20)]
public string Mobile { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime DateAdded { get; set; }
[DataType(DataType.DateTime)]
public DateTime? LastLoginDate { get; set; }
public bool? OffersOptIn { get; set; } //any offers we may have with us or partners
public bool? NewsOptIn { get; set; } //newsletter
public bool? SubscriptionOptIn { get; set; } //account, technical, renewal notices, pp invoices, pp receipts
public bool? OrderOptIn { get; set; } //orders - workflow notices
[DataType(DataType.DateTime)]
public DateTime? LastUpdatedAccountDate { get; set; } //Last time user updated contact info
}
Try this. It will cast your query into your view model.
var viewModel = _db.UserProfiles
.Where(x => admins.Contains(x.UserName))
.Select(x => new AdminsAndAgentsListVM {
FirstName = x.FirstName,
LastName = x.LastName,
UserId = x.UserId});
You're passing the view your query, not your model.
Execute the query as you have it
var query = _db.UserProfiles
.Where(x => admins.Contains(x.UserName));
Then instantiate and populate your view model
var viewModels = new List<AdminsAndAgentsListVM>();
foreach (var item in query)
{
var viewModel = new AdminsAndAgentsListVM();
viewodel.FirstName = item.FirstName;
viewodel.LastName = item.LastName;
viewodel.UserId = item.UserId;
viewModels.Add(viewModel);
}
return View(viewModels);
This assumes, of course, that a UserProfile and AdminsAndAgentsListVM have matching properties.
Change your return line to:
return View(viewModel.AsEnumerable());
You aren't selecting your ViewModel. You need to do a Select(x => new AdminsAndAgentsListVM on your query. I would also do ToList() on there.