display data from 2 joined tables in MVC - c#

I have two tables,and i am performing a left join on them,i want the resultant table to be displayed in my View, any ideas how to do that?
This is my controller section
public Actionresult display()
{
var joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new { ID=c.Id,CategoryName= c.CategoryName,SubCategoryName=n.SubCategoryName }).ToList();
return(joindata);
}
I cannot display it as a strongly typed view as the resultant table is join of two tables, so any ideas how to do it?

i want the resultant table to be displayed in my View
That's the wrong approach.
In MVC your views do not show 'a table' but they show 'a model'.
That change in wording is the solution: create a class for your joined records and create a strongly-typed View for that Model.

All you want is a View model..Make a class like ViewModelResult or anything like that
public class ViewModelResult
{
public YourTable1 YourTable1{get;set;}
public YourTable1 YourTable2 {get;set;}
}
and then make youir code as below :-
public Actionresult display()
{
ViewModelResult joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new ViewModelResult { ID=c.Id,CategoryName= c.CategoryName,SubCategoryName=n.SubCategoryName }).ToList();
return view(joindata);
}
and in your view bind ViewModelResult as model and there you can perform whatever you want to like showing data in grid via foreach and etc.

One way would be to use ViewBag
so instead of passing model to the view do this.
ViewBag.Joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new { ID=c.Id,CategoryName= c.CategoryName,SubCategoryName=n.SubCategoryName }).ToList();
You will then be able to access ViewBag from the view this is very dirty solution.
Example
#ViewBag.Joindata[0].ID
will contain first record.
Other option would be instead of using dynamic object to create model that does represent your viewModel and use strongly typed view, this would be more appropriate solution to your problem.
public class ThatSpecificViewModel
{
public ThatSpecificViewModel(int id, string categoryName, string subCategoryName)
{
this.ID = id;
this.CategoryName = categoryName;
this.SubCategoryName = subCategoryName;
}
public int ID { get; set; }
public string CategoryName { get; set; }
public string SubCategoryName { get; set; }
}
and select it this way
var joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new ThatSpecificViewModel(c.Id,c.CategoryName,n.SubCategoryName )).ToList();

Related

How to use Join in select statement using linq expression

Hi Have a two tables that have a relationship like below ..
class Boothtable
{
public int BoothId {get;set;}
public string BoothName {get;set;}
public double Price {get;set;}
public int RoomId{get;set;}
}
class RoomTable
{
public int RoomId {get;set;}
public string RoomName{get;set;}
public sting Location {get;set;}
}
I am trying to write a join query using LINQ to join the the boothtable to the roomstable on RoomId and return all properties from the boothtable and only return the RoomName property from the Rooms table. is there anyone that can direct me how to achieve this?
So far i have a linq exp like this but it only returns all properties from the booths table.
var getData = from boothtable in context.Boothtable
join roomtable in context.RoomTable
on boothtable.RoomId equals roomtable.RoomId
where boothtable.BoothId == someId
select boothtable;
If you want to retrieve properties from both you must project an object containing data from both. This uses an anonymous object to do so:
select new { boothtable, roomtable }
If you want to return this collection of anonymous objects from a method then define a custom object with the two properties and instantiate it
select new YourCustomObjet { Booth = boothtable, Room = roomtable }
If you only want some of the properties (missed it at first) then as you project the objects in the examples above just project the specific fields:
select new
{
boothtable.BoothId,
boothtable.BoothName,
boothtable.Price,
roomtable.RoomName
}
Notice that if it is an anonymous object and the desired property names are the same, no need to explicitly define a name
Try this -
var getData = (from boothtable in context.Boothtable
join roomtable in context.RoomTable
on boothtable.RoomId equals roomtable.RoomId
where boothtable.BoothId == someId
select new
{
BoothId = boothtable.BoothId,
BoothName = boothtable.BoothName,
Price = boothtable.Price,
RoomName = roomtable.RoomName
});

Performing join using linq query in Entity Framework

I'm trying to select some data from a table in my database using a join in a linq query, but I can't seem to grasp how to save it to the list of DTO's that I would like to return.
I've been looking at this post for directions at using the lambda expression: C# Joins/Where with Linq and Lambda but it seems like that guy is trying to accomplish something slightly different than me; I want to compare the value CPR (from the table Coworkers) and the value CPR (From the table Duties) and select all of those where the Projektname (from the table Duties) are equal to the string projektname.
What I've written so far of the method is this:
public List<CoworkerDTO> GetCoworkers(string projektname)
{
_coworkerlist = new List<CoworkerDTO>();
using (var context = new F17ST2ITS2201608275Entities())
{
var dataset =
from co in context.Coworkers
join du in context.Duties on co.CPR equals du.CPR
where du.Projektname == projektname
select new {Coworkers = co};
foreach (var element in dataset.ToList())
{
_coworkerlist.Add(element);
}
}
return _coworkerlist;
}
The CoworkerDTO looks like this:
class CoWorkerDTO
{
public string Fornavn { get; set; }
public string Efternavn { get; set; }
public int Alder { get; set; }
public string CPR { get; set; }
public decimal AntalTimer { get; set; }
}
The table Coworkers has a column that corresponds to each of the properties above, so I guess my question is how to somehow convert the selection that I get into a list of the CoworkerDTOs.
Sorry for the long post, and if my english is a bit confusing, as it's not my first language.
Thanks in advance :)
You should convert Coworkers entity into CoWorkerDTO. You can do it manually (assume properties have same names and types):
var dtos =
from co in context.Coworkers
join du in context.Duties on co.CPR equals du.CPR
where du.Projektname == projektname
select new CoWorkerDTO {
Fornavn = co.Fornavn,
Efternavn = co.Efternavn,
Alder = co.Alder,
CPR = co.CPR,
AntalTimer = co.AntalTimer
};
return dtos.ToList();
Or you can use something like AutoMapper Queryable Extensions to do that projection automatically:
Mapper.Initialize(cfg =>
cfg.CreateMap<Coworkers, CoWorkerDTO>());
And query with projection will look like
var entities =
from co in context.Coworkers
join du in context.Duties on co.CPR equals du.CPR
where du.Projektname == projektname
select co;
return entities.ProjectTo<CoWorkerDTO>().ToList();

Join and override a model value using LINQ

I want to override a string ID with a join when doing a query. At the moment I am using the database model as the view model, as Visual Studio automates. This is quite acceptable for my use, but I need to display the username not their ID. For a single candidate I have a achieved it thus for the Details view:
var ret = (from c in db.candidates
where c.candidate_id == id
join u in db.Users on c.createdby equals u.Id
select new { c, u.UserName }
).FirstOrDefault();
candidate theCandidate = ret.c;
theCandidate.createdby = ret.UserName; // Override with the username
Could I have done the theCandidate.createdby = ret.UserName within the LINQ query?
Or should I approach this differently?
(it seems to me that creating passing the anonymous type to the view is not ideal)
Edit
Or should I just change the database model to have the join implicitly:
class Candidate
{
...
[ForeignKey("createdby")]
public virtual ApplicationUser CreatedByUser { get; set; }
}
This is a typical case that proves the usefulness of ViewModels: your view needs something else compared to what your database delivers.
Also, your code puts something in createdby that does not belong there, assuming the datatypes allow it (what if createdby is an int?)
Perhaps it's time to get used to using this:
public class CandidateViewModel
{
// add properties that the View needs from database candidates table
string Username { get; set; } // the additional prop
public CandidateViewModel(candidate c, string username)
{
// ... obvious code here
}
}
And then
var ret = (from c in db.candidates
where c.candidate_id == id
join u in db.Users on c.createdby equals u.Id
select new CandidateViewModel(c, u.UserName)
).FirstOrDefault();
Edit:
With the new foreign key in place you could try something like this:
// using System.Data.Entity; <-- add this on top
var ret = (from c in db.candidates
where c.candidate_id == id
select c
)
.Include("CreatedByUser") // or "createdby" - not sure
.FirstOrDefault();
With that in place, try using ret.CreatedByUser.UserName in the controller, or even in the View. Note that my EF is a bit rusty, so you may have to try some things to get it right.

How to declare List of anonymous type

In this official ASP.NET MVC Core tutorial they have defined a ViewModel as follows where Movie is a class. Then in the Index(...) method (towards the end of the tutorial they are populating the ViewModel from a simple LINQ query as shown in the Controller below:
ViewModel:
public class MovieGenreViewModel
{
public List<Movie> movies;
public SelectList genres;
public string movieGenre { get; set; }
}
Controller:
public async Task<IActionResult> Index(string movieGenre, string searchString)
{
// Use LINQ to get list of genres.
IQueryable<string> genreQuery = from m in _context.Movie
orderby m.Genre
select m.Genre;
var movies = from m in _context.Movie
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (!String.IsNullOrEmpty(movieGenre))
{
movies = movies.Where(x => x.Genre == movieGenre);
}
var movieGenreVM = new MovieGenreViewModel();
movieGenreVM.genres = new SelectList(await genreQuery.Distinct().ToListAsync());
movieGenreVM.movies = await movies.ToListAsync();
return View(movieGenreVM);
}
Question: In the above ViewModel the property movies was of type List<movie> and the query used in the Index(...) method was a simple query involving just one table (movies). But what if the query involves an INNER JOIN with two tables and hence returns an object of anonymous type as shown below. In that case how would we declare a list of anonymous type List<.?.> in a ViewModel and then populate that property of type List<.?.> in the ViewModel? In the example below I defined a ViewModel with List<dynamic> or List<object> and tried myViewModel.list = leftOuterJoinQuery.ToListAsync() but got the error as Cannot implicitly convert type 'List<<anonymous type: int CategoryId, string CategoryName, string ProdName>>>' to 'System.Collections.Generic.List<dynamic>:
LINQ INNER JOIN Query:
var innerJoinQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID
select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence
UPDATE:
As #StephenMuecke suggested below, the problem can be solved by creating another View Model containing the properties returned by my query. But I'm thinking if there is a way to avoid creating an extra View Model.
There is way to accomplish it without creating new VM, but i'm not sure it is the right way.
First, I'll turn the VM as below
public class MovieGenreViewModel
{
public ICollection movies;
public SelectList genres;
public string movieGenre { get; set; }
}
Define a helper method to compose list from anonymous objects
public static List<T> CreateList<T>(params T[] elements)
{
return new List<T>(elements);
}
Finally, change the line inside Index() method
from
movieGenreVM.movies = await movies.ToListAsync();
to
var output = await innerJoinQuery.ToListAsync();
movieGenreVM.movies = CreateList(output);
Hope this helps.

How do I return data from joined tables through subsonic's objects?

I'm using ActiveRecord on Subsonic 3 and I effectively want to do this:
select * from foo
left outer join bar on bar.Id = foo.barId
where foo.someProperty = 2
I've written a stored procedure to fetch the data but Subsonic has only created objects to hold the columns from foo and bar.
What's the best way of returning the data into a single object so I can just bind it. Ideally I want it to be in a list<> but without writing my own class, there doesn't seem to be a way provided by subsonic.
You have a couple options here...
You could create a database view that does your join, and have SubSonic generate a data type for your view, then your select would be just like selecting from any other table.
Alternatively, you could use a Linq expression to do the join into an anonymous or dynamic type (if you are using .net 4) For example:
public List<dynamic> LoadData(int id)
{
var data = from f in db.Foo
from b in db.Bar.Where(x => x.Id == f.BarId).DefaultIfEmpty()
where f.SomeProperty == id
select new
{
SomeProperty = f.Something,
AnotherProperty = b.SomethingElse
};
return data.Cast<dynamic>().ToList();
}
Of course another alternative is to do the Linq expression above, but define your own class to hold the returned data, and select into it.
public class MyData
{
public string SomeProperty { get; set; }
public string AnotherProperty { get; set; }
}
public List<MyData> LoadData(int id)
{
var data = from f in db.Foo
from b in db.Bar.Where(x => x.Id == f.BarId).DefaultIfEmpty()
where f.SomeProperty == id
select new MyData()
{
SomeProperty = f.Something,
AnotherProperty = b.SomethingElse
};
return data.ToList();
}

Categories

Resources