LINQ EF Grouping and Include exception - c#

Need help in creating LINQ query to group and filter with related entities.
Here is my model classes.
public class Application
{
[DisplayName("Application")]
public int ApplicationId { get; set; }
[DisplayName("Application")]
public string Name { get; set; }
public List<DashboardEntry> DashboardEntries { get; set; }
}
public class Cluster
{
[DisplayName("Cluster")]
public int ClusterId { get; set; }
[DisplayName("Cluster")]
public string Name { get; set; }
}
[Bind(Exclude = "AlbumId")]
public class DashboardEntry
{
[ScaffoldColumn(false)]
public int DashboardEntryId { get; set; }
public int ClusterId { get; set; }
public int ApplicationId { get; set; }
public HealthStatusIndicator Status { get; set; }
public string Incident { get; set; }
public string Remarks { get; set; }
public virtual Cluster Cluster { get; set; }
public virtual Application Application { get; set; }
}
Index action method is as follows
public ActionResult Index()
{
//var dashboardEntries = db.DashboardEntries.Include(d => d.Application).Include(d => d.Cluster);
var dashboardEntries = db.DashboardEntries
.Include(d => d.Application)
.Include(d => d.Cluster)
.GroupBy(d => d.Application);
return View(dashboardEntries.ToList());
}
In the view, model declaration is as below.
#model IEnumerable<HealthCheckIndex.Models.DashboardEntry>
I'm getting an error
The model item passed into the dictionary is of type
'System.Data.Entity.Infrastructure.DbQuery1[System.Linq.IGrouping2[HealthCheckIndex.Models.Application,HealthCheckIndex.Models.DashboardEntry]]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`1[HealthCheckIndex.Models.DashboardEntry]'.
If I change the model declaration in view as below, I'm getting a another error that Cluster is not accessible.
#model IEnumerable>
I want to group the dashboard entries into different applications and filter the groups by choosing the max dashboard entry from each group.

The type that you are passing currently to the view does not match the specified type
#model IEnumerable<HealthCheckIndex.Models.DashboardEntry>
Currently you are passing something like a dictionary where the key is Application and the value is a IEnumerable of HealthCheckIndex.Models.DashboardEntry
In order to make it you have one of 2 options:
Replace the last line of the controller action with
return View(dashboardEntries.SelectMany(c=> c).ToList());
Change model definition in the view to match the list your returning

Related

Relational Database SQL Query in Asp.NET Core

public async Task<List<Note>>ShowAssigned()
{
return await _context.Notes
.Where(x => x.List.OwnerId != x.OwnerId)
.ToListAsync()
}
I get no syntax εrrors, but it seems you can't access attributes from related Data in this way.
Basically the goal is: A user creates a List, then some Notes for this List. Then he should be able to assign one of that Notes to another User. When that other User logs on, he should be able to see that new Note that was assigned to him.
Can anyone help me out with this?
public class List
{
public Guid ListId { get; set; }
public string OwnerId { get; set; }
public List<Note> Notes { get; set; }
}
public class Note
{
public Guid ID { get; set; }
public string OwnerId { get; set; }
[ForeignKey("ListId")]
public Guid ListId { get; set; }
public List List { get; set; }
}
And the context class:
public DbSet<Note> Notes { get; set; }
public DbSet<List> Lists { get; set; }
When i try to access Data the same way in a view like that:
#model List<Project.Models.Note>
#foreach (var item in Model)
{
if (item.List.OwnerId == item.OwnerId)
i get this error when running the web app (no syntax errors):
NullReferenceException: Object reference not set to an instance of an object
First write your model classes as follows:
public class List
{
public Guid ListId { get; set; }
public string OwnerId { get; set; }
public virtual List<Note> Notes { get; set; }
}
public class Note
{
public Guid ID { get; set; }
public string OwnerId { get; set; }
[ForeignKey("List")] // Not ListId, its List
public Guid ListId { get; set; }
public virtual List List { get; set; }
}
If your project is on ASP.NET Core < 2.1
Then write your query as follows:
await _context.Notes.Include(n => n.List).ToListAsync()
If your project is on ASP.NET Core >= 2.1
Then in the ConfigureServices() method in Startup class:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
Don't forget to install appropriate version of Microsoft.EntityFrameworkCore.Proxies nuget package because UseLazyLoadingProxies() resides in this package.
Then write your query as follows:
await _context.Notes.ToListAsync()
So I found the answer to my problem, in some parts with the help of TanvirArjel (but i basically did it differently)
public async Task<List<Note>> GetAssignedItemsAsync(ApplicationUser user)
{
var lists = await _context.Lists.Include(l => l.Notes).Where(x => x.OwnerId != user.Id).ToListAsync();
var notesListe = new List<Note>();
foreach (List l in lists)
{
foreach (Note n in l.Notes)
{
if (n.OwnerId == user.Id)
{
notesListe.Add(n);
}
}
}
return notesListe;
}

How to return view with Outer Joins from two tables in MVC Razor

I've two tables as Models in my project Like:-
public partial class TblAlbum
{
public int ID { get; set; }
public string Album { get; set; }
public string Artists { get; set; }
public Nullable<int> AudioID { get; set; }
public Nullable<int> VideoID { get; set; }
public virtual TblVideo TblVideo { get; set; }
public virtual TblAudio TblAudio { get; set; }
}
and
public partial class TblAudio
{
public int ID { get; set; }
public string Name { get; set; }
public string Alt { get; set; }
public string Artist { get; set; }
public string Image { get; set; }
public int LangID { get; set; }
public virtual TblLanguage TblLanguage { get; set; }
public virtual ICollection<TblAlbum> TblAlbums { get; set; }
}
Now I've Made a ViewModel as GetDetailsVM that have access to both tables and has the LINQ Query as:-
public class GetDetailsVM
{
private MusicEntities db = new MusicEntities();
public IEnumerable<dynamic> GetAudio()
{
var AudioList = from au in db.TblAudios
join al in db.TblAlbums on au.ID equals al.AudioID into ar
from al in ar.DefaultIfEmpty()
select new { au,al };
return AudioList.ToList();
}
}
My ViewModel(AudioAlbumVM) to read Getaudio() should be something like this:-
public class AudioAlbumVM
{
public IEnumerable<dynamic> AudioObjList { get; set; }
public string AlbumName { get; set; }
}
Now I want to access this ViewModel in my controller and then use it in my cshtml.
My Controller:-
public ActionResult Audio()
{
ViewBag.Title = "Audio";
var AudioSummary = new GetDetailsVM();
var viewModel = new AudioAlbumVM
{
AudioObjList = AudioSummary.GetAudio().First()
};
return View(viewModel);
}
UPDATE
My View(Audio.cshtml) is as follows:-
#model GarhwalMusic.Model‌​s.AudioAlbumVM.AudioObjList
<a class="art" href="single.html"> #Model.AudioObjList</a>
I was going through this question LINQ left join only works in the ActionResult but I'm completely lost . Need help and explanation on how to create AudioAlbumVM using another ViewModel(GetDetailsVM) then in controller and then in cshtml. Any help is greatly appreciated!!
Taking this from your comments
Now I'm getting this error:- The model item passed into the dictionary is of type 'GarhwalMusic.Models.AudioAlbumVM', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[GarhwalMusic.Model‌​s.AudioAlbumVM]'.
Problem is with this code
var viewModel = new AudioAlbumVM //its a single model
{
AudioObjList = AudioSummary.GetAudio().First()
};
But it seems like your view is expecting a model of type IEnumerable<GarhwalMusic.Model‌​s.AudioAlbumVM> or (List<GarhwalMusic.Model‌​s.AudioAlbumVM>). You need to either pass a List<GarhwalMusic.Model‌​s.AudioAlbumVM> to your view or change your view to accept a model of type GarhwalMusic.Model‌​s.AudioAlbumVM.
But looking at your code I assume that you are trying to pass the collection of AudioObjList into your View. So your view must accept a model of type
GarhwalMusic.Model‌​s.AudioAlbumVM.AudioObjList and you need to just Pass in only the viewModel.AudioObjList from your controller.
EDIT 1: After you added more details of your view, You are making a mistake in the way you are accessing the data.
your Model.AudioObjList is of type IEnumerable<dynamic> and how can you just print something on the Vie like #Model.AudioObjList ?? I am referring to this code
<a class="art" href="single.html"> #Model.AudioObjList</a>
You need to typecast this dynamic type into what ever object you have created in your linq and then extract its property value. Something like
#((yourObject)Model.AudioObjList[0]).propertyName
(yourObject)Model.AudioObjList[0] type casts your dynamic data into a type yourObject.
you need [0] because your AudioObjList is of type IEnumerable<dynamic>. So taking the first data.

Fill dropdown list from database in create view

How can I fetch and insert data at a specific time in one view in mvc razor view? I mean to fill a dropdown list from the database in create view.
I want to fill the following when I add the subject and cheater models.
department list
semester list
standard list
stream list
cheater model:
namespace firstapp.Models
{
public class chepter
{
[ForeignKey("dip_id")]
public int dipart_id { get; set; }
public int chep_id { get; set; }
public string subject { get; set; }
public string chepter { get; set; }
public List<dipartment> dipartlist { get; set; }
public List<dipartment> stdlist { get; set; }
public List<dipartment> semlist { get; set; }
public List<dipartment> stremlist { get; set; }
}
}
department model:
namespace firstapp.Models
{
public class dipartment
{
public int dip_id { get; set; }
public string dipart { get; set; }
public string std { get; set; }
public string sem { get; set; }
public string strem { get; set; }
}
}
#Html.DropDownListFor(model => model.dipart_id, new SelectList(Model.dipartlist.Select(s => new SelectListItem() { Value = s.dip_id, Selected = false, Text = s.dipart })), "Select")
Change your model so the list property is a selectlist:
public SelectList<dipartment> dipartlist { get; set; }
Then, when you populate the model call a service class method(you might not have a service layer, I just prefer to not have database calls in the controller)
dipartlist = _departmentService.GetAsSelectList();
The GetAsSelectList service method looks like this:
public SelectList GetAsSelectList()
{
return (from d in _context.Set<department>().OrderBy(x => x.dipart)
select new
{
Id = d.dipart_id,
Name = d.dipart
}).ToList();
}
And finally your view:
#Html.DropDownListFor(model => model.dipart_id, Model.dipartlist)
This technique means you don't have linq in either the view or controller. Also as you're only creating the selectlist in one place (the service), you can cache it with MemoryCache to prevent multiple requests for the same data. And as it looks like you're populating 4 selectlists, this might be useful.

how to map from a domain model to a view model from MVC controller with AutoMapper

I am trying to figure out how AutoMapper works in creating a map from the domain model to the view model with a complex collection.
Within my domain model (Search.Domain) ,
I have the following:
namespace Search.Domain.Model
{
public class Result
{
public int SearchTime { get; set; }
public List<ResultDetails> Context { get; set; }
}
public class ResultDetails
{
public string Entity { get; set; }
public string Jurisdiction { get; set; }
public DateTime DateReported { get; set; }
public string Description { get; set; }
public DateTime DateEntered { get; set; }
public string AssociatedLink { get; set; }
public int Relevance { get; set; }
}
}
with the MVC project (Search.WebUI) I have the following:
namespace Search.WebUI.Models
{
public class ResultViewModel
{
public int SearchTime { get; set; }
public List<ResultDetails> Context { get; set; }
}
public class ResultDetails
{
public string Entity { get; set; }
public string Jurisdiction { get; set; }
public DateTime DateReported { get; set; }
public string Description { get; set; }
public DateTime DateEntered { get; set; }
public string AssociatedLink { get; set; }
public int Relevance { get; set; }
}
}
Within the controller (HomeController.cs)
namespace Search.WebUI.Controllers
{
public class HomeController : Controller
{
private ISearchResultManager sr = new ResultManager();
public ActionResult Index()
{
ResultViewModel searchresults;
var results = sr.GetSearchResults(5);
Mapper.CreateMap<Search.Domain.Model.Result, ResultViewModel>();
searchresults = Mapper.Map<Search.Domain.Model.Result, ResultViewModel>(results);
return View("Home", searchresults);
}
}
}
The error message that is being generated when run is:
Missing type map configuration or unsupported mapping.
Mapping types:
ResultDetails -> ResultDetails
Search.Domain.Model.ResultDetails -> Search.WebUI.Models.ResultDetails
Destination path:
ResultViewModel.Context.Context.Context0[0]
Source value:
Search.Domain.Model.ResultDetails
In looking at this it appears that the nested List<ResultDetails> is causing an issue but I don't know what I am supposed to do to handle this type of mapping.
Is it correct to go all the way back into the domain for reference to the type? This seems as if I am pulling the domain into the UI which I would not want to do?
Is there another option for mapping domain models to view models in the UI? Basically I was hoping to have a view model within the UI that I could extend beyond the domain model for UI purposes and not put a reference to the domain model.
I am new to autoMapper so this entire thing may be wrong? I would appreciate any suggestions or guidance.
AutoMapper doesn't look at all potential child mapping when mapping a containing class. You need to explicitly add a mapping for the ResultDetails as well:
Mapper.CreateMap<Search.Domain.Model.Result.ResultDetails,
Search.WebUI.Models.ResultDetails>();

MVC 4 Simple Populate DropDown from database model

I feel a bit stupid.
I'm trying to get a hang of MVC 4, using boxing as a functional example.
I have WeightCategories in the database (Heavyweights, etc), and Boxers.
Seem simple. The relation is a boxer has a current weight category, but when I edit, I want it to be able to change it with a drop down.
I understand how to do it if it's a list I've made myself in the code, but I have problem understanding how to "load" the list from the WeightCategory table and show it in the view/model of the boxer.
So, here is my code for the WeightCategory item:
[Table("WeightCategories")]
public class WeightCategory
{
[Key]
public int WeightCategoryId { get; set; }
public WEIGHT_CATEGORIES WeightCategoryType { get; set; }
[Display(Name = "Weight Category Name")]
[Required]
[MinLength(5)]
public string Name { get; set; }
[Display(Name = "Weight Limit In Pounds")]
public int? WeightLimit { get; set; }
}
Here is the code for the boxer item
[Table("Boxers")]
public class Boxer
{
[Key]
public int BoxerId { get; set; }
public WeightCategory CurrentWeightCategory { get; set; }
[Required]
public string Name { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public int Draws { get; set; }
public int Kayos { get; set; }
}
In the view, I'm really not sure how to tackle that, I'm pretty sure it's not automatic and I need to load the table somewhere in the controller maybe... I'm looking for best practice or something.
Something like that in the view at the end:
#Html.DropDownListFor(model => model.CurrentWeightCategory.WeightCategoryId,
new SelectList(Model.WeightCategories, "WeightCategoryId", "Name",
Model.WeightCategories.First().WeightCategoryId))
You could design a view model:
public class MyViewModel
{
public Boxer Boxer { get; set; }
public IEnumerable<SelectListItem> WeightCategories { get; set; }
}
and then have your controller action populate and pass this view model to the view:
public ActionResult Edit(int id)
{
var model = new MyViewModel();
using (var db = new SomeDataContext())
{
// Get the boxer you would like to edit from the database
model.Boxer = db.Boxers.Single(x => x.BoxerId == id);
// Here you are selecting all the available weight categroies
// from the database and projecting them to the IEnumerable<SelectListItem>
model.WeightCategories = db.WeightCategories.ToList().Select(x => new SelectListItem
{
Value = x.WeightCategoryId.ToString(),
Text = x.Name
})
}
return View(model);
}
and now your view becomes strongly typed to the view model:
#model MyViewModel
#Html.DropDownListFor(
x => model.Boxer.CurrentWeightCategory.WeightCategoryId,
Model.WeightCategories
)

Categories