Select 2 Column of 2 Table - c#

I have 2 tables:
Staff: (ID, NAME)
Salary: (ID, VALUE)
My problem is:
How could I write in query syntax and method syntax in controller and return to the View(), like this in SQL query:
select a.NAME, b.VALUE
from Staff a, Salary b
where a.ID = b.ID
How could I declare in view like:
#model IEnumerable<Project.Model.xxxx>
This is the way I do with one table in IndexController:
LinQDataContext data = new LinQDataContext();
public ActionResult Index(int id)
{
var staffWithTheirSalary = data.Staff
.Select(a => a)
.Where(a => a.ID == id);
}
Index view:
#model IEnumerable<Project.Model.Staff>
#foreach(var item in Model)
{
#item.Name
}

You can have your view model to be IEnumerable<StaffSalaryViewModel> where StaffSalaryViewModel is:
public class StaffSalaryViewModel
{
public int StaffId { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; } // Not sure of the type
}
Then to extract the list:
Using Linq method syntax:
var result = data.Staff
.Join(data.Salary,
staff => staff.ID,
salary => salary.ID,
(staff, salary) => new StaffSalaryViewModel {
StaffId = staff.ID,
Name = staff.NAME,
Salary = salary.VALUE
});
Using Linq query syntax:
var result = from staff in data.Staff
join salary in data.Salary on staff.ID equals salary.ID
select new StaffSalaryViewModel {
StaffId = staff.ID,
Name = staff.NAME,
Salary = salary.VALUE
};
UPDATE
It looks like the VALUE field in your Salary table allows null values. If so just make the corresponding property in your view model nullable, like so (provided it is a decimal type):
public class StaffSalaryViewModel
{
public int StaffId { get; set; }
public string Name { get; set; }
public decimal? Salary { get; set; } // Nullable/optional decimal
}
Or if you want to set StaffSalaryViewModel.Salary to 0 If VALUE is null, change your query to have:
Salary = salary.VALUE ?? 0

Because tables are being joined, I prefer using the query syntax. The controller would use the LinQDataContext to get at the data, but when creating the C# Object I would recommend creating a Class that is only used for displaying the data (in the example its called MySalaryModel).
public ActionResult Index(int id)
{
IEnumerable<Project.Model.MySalaryModel> staffWithTheirSalary = from staff in data.Staff
join salary in data.Salary on staff.Id equals salary.Id
select new Project.Model.MySalaryModel
{
Id = staff.Id,
Name = staff.Name,
Salary = salary.Value,
};
return View(staffWithTheirSalary);
}

use ViewModel,
you can create Viewmodel Folder in project and Create class into like this.
public class StaffSalaryViewModel
{
public int StaffId { get; set; }
public int SalaryeId { get; set; }
public string StaffName { get; set; }
public string SlaaryName { get; set; }
}
in action :
public ActionResult Index(int id)
{
StaffSalaryViewModel staffWithTheirSalary = (from itemStaff in data.Staff join itemSalary in data.Salary in itemStaff.id equal itemSalary.id where itemSalary.id=id select new StaffSalaryViewModel {Salaryname=itemSalary.Name,StaffName=itemStaff.Name,SalaryId=itemSalary.Id,itemStaff.Id}).ToList();
}
in View
#model IEnumerable<Project.ViewModel.StaffSalaryViewModel>
#foreach(var item in Model)
{
#item.SalaryName
}

Related

MVC5/C#: Pass LINQ inner Join query to view model

i'm trying to pass data(.tolist) in inner join query to viewmodel, to use these data in view that contain also a partial view that needs data from viewModel.
public ActionResult IndexAdmin()
{
int userId = (int)Session["UserID"];
userInfo = _context.UserInfo.Find(userId);
var AllTours= (from p in _context.PostsInfo //Why this doesn't return two records
join r in _context.Region
on p.RegionId equals r.Id
where r.CountryId == 1
select new
{
RegionName = r.CountryId,
ImageName = p.ImageName,
}).Distinct().ToList();
//Here i need to define IndexAdminViewModel to populate tours and userInfo.username
return View(AllTours);
}
This is the IndexAdminViewModel:
public class IndexAdminViewModel
{
public string UserName { get; set; }
public string RegionName{get;set;}
public string ImageName {get;set;}
}
The view(IndexAdmin.cshtml)
#Html.Partial("_BarPartialAdmin", Model)
foreach (var post in Model)
{
<img src="~/images/#post.ImageName" alt="QfirstImage">
<h2>post.RegionName</h2>
}
The partial view will only needs the username to display it for once so i used to pass the model to partial view in order to use the username property, the RegionName and ImageName is a collection so that i can iterate over them and get teh values some way like use them in a table.
My question is how to pass the inner join query results AND theuserinfo.username to viewModel to use them in the view???????
You need to create 2 view models
public class ToursViewModel
{
public string RegionName { get; set; }
public string ImageName {get; set; }
}
public class IndexAdminViewModel
{
public string UserName { get; set; }
public IEnumerable<ToursViewModel> Tours {get;set;}
}
Then in the controller
public ActionResult IndexAdmin()
{
int userId = (int)Session["UserID"];
userInfo = _context.UserInfo.Find(userId);
IEnumerable<ToursViewModel> tours = (
from p in _context.PostsInfo
join r in _context.Region
on p.RegionId equals r.Id
where r.CountryId == 1
select new ToursViewModel
{
RegionName = r.CountryId,
ImageName = p.ImageName
});
IndexAdminViewModel model = new IndexAdminViewModel
{
UserName = userInfo.username,
Tours = tours
};
return View(model);
and in the view
#model IndexAdminViewModel
....
<h1>#Model.UserName</h1>
#foreach (var tour in Model.Tours)
{
<img src="~/images/#tour.ImageName" alt="QfirstImage">
<h2>#tour.RegionName</h2>
}
I you need to pass different objects to the view you basically have two options:
Create a composite class and use that as model
var allTours=
from p in _context.PostsInfo //Why this doesn't return two records
join r in _context.Region
on p.RegionId equals r.Id
where r.CountryId == 1
select new PostAndRegion
{
Region = r.CountryId,
Post= p.ImageName,
};
var model = new MyCompositeModel
{
PostsAndRegions = allTours.ToArray(),
UserInfo = null // or get from where you want to
};
return View(model);
with
public class PostAndRegion
{
public Post Post{get;set;}
public Region Region {get;set;}
}
public class MyCompositeModel
{
public IList<PostAndRegion> PostsAndRegions{get;set;}
public UserInfo MyUserInfo{get;set;}
}
Put some of the data in the ViewBag. see http://www.tutorialsteacher.com/mvc/viewbag-in-asp.net-mvc

Printing output from my linq query in html

I am using asp.net core entity framework. I am trying to print out the results of my query into my html. Here is my code.
This is my .cs file
ViewBag.User_Has_Products = (from user_products in _context.Users_Has_Products
join user in _context.Users on user_products.users_id equals user.id
join product in _context.Products on user_products.products_id equals product.id
select new { name = user.name, product = product.name,
quantity = user_products.quanitity, date = user_products.created_at});
foreach(var item in ViewBag.User_Has_Products)
{
System.Console.WriteLine($"{item.name}"); //this output is correct
}
Here is my .cshtml page
#{
if(ViewBag.User_Has_Products != null)
{
foreach(var item in ViewBag.User_Has_Products)
{
<p>#$"{item.name}"</p> //trying to print it out here
}
}
}
I also tried <p>#item.name</p> but got an object does not contain a definition for name error.
You don't need the string quotes in your .cshtml file, just use it as is, see the following example:
#{
if(ViewBag.User_Has_Products != null)
{
foreach(var item in ViewBag.User_Has_Products)
{
<p>#item.name</p>
}
}
}
But the better recommendation is to build a model and send it to your view, like this:
public class UserProductsViewmodel() {
public string Name { get; set; }
public string Product { get; set; }
public int Quantity { get; set; }
public string Date { get; set;}
}
and use it in your .cs file like this:
ViewBag.User_Has_Products = (from user_products in _context.Users_Has_Products
join user in _context.Users on user_products.users_id equals user.id
join product in _context.Products on user_products.products_id equals product.id
select new UserProductsViewmodel() {
Name = user.name, Product = product.name,
Quantity = user_products.quanitity, Date = user_products.created_at
});
And in your .cshtml like this:
#{
if(ViewBag.User_Has_Products != null)
{
foreach(UserProductsViewmodel item in ViewBag.User_Has_Products)
{
<p>#item.Name</p>
}
}
}
Check this article on Dependency injection into views which will a better way than using ViewBag.
Controller action snippet
public IActionResult GetProducts()
{
var products = (from user_products in _context.Users_Has_Products
join user in _context.Users on user_products.users_id equals user.id
join product in _context.Products on user_products.products_id equals product.id
select new UserProductsViewmodel() {
Name = user.name, Product = product.name,
Quantity = user_products.quanitity, Date = user_products.created_at
});
return View(products);
}
View snippet(.cshtml)
#model IEnumerable<namespace.ProductModel>
...
#foreach (var item in Model)
{
<p>#item.Bar</p>
}
ViewModel
public class ProductModel() {
public string Name { get; set; }
public string Product { get; set; }
public int Quantity { get; set; }
public string Date { get; set;}
}

can't get values in foreach from Viewbag mvc

I'm trying to get values from userList viewbag.i can't figure out the solution. Error is:
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll but was not handled in user code
Additional information: 'object' does not contain a definition for 'name'
though in ViewBag.userList contains data (2 objects) which i can see while debugging
#foreach (var aUser in ViewBag.userList)
{
<tr>
<td>#aUser.name</td>
<td>#aUser.username</td>
.....
<td>#Html.ActionLink("Edit", "UserEdit","Users")</td>
<td>#Html.ActionLink("Delete", "UserDelete", "Users")</td>
</tr>
}
I have a superclass and a childclass
superclass
public partial class user
{
public int id { get; set; }
public string name { get; set; }
public string username { get; set; }
...
public string user_role { get; set; }
}
childclass
public class UserSub: user
{
public string CreatedUserName { get; set; }
public string ModifiedUserName { get; set; }
}
In my controller i used linq to get values from database and stored it to Viewbag.userList. My controller function is
public ActionResult UserList()
{
IEnumerable<user> users = null;
users = dbEntities.users.ToList();
if (users != null)
{
var userLists = (from a in users join b in users on a.created_user_id equals b.id select new { a.name, a.username, a.password, a.user_role, a.is_enable, a.is_approved, CreatedUserName = b.name, a.create_time, a.is_android, a.device_id }).ToList();
ViewBag.userList = userLists;
}
return View();
}
tried List<UserSub> users=ViewBag.userList....getting error too
Use a ViewModel to share data between view and controller.
For example, first create the ViewModel:
public class userViewModel{
public int id { get; set; }
public string name { get; set; }
public string username { get; set; }
public string user_role { get; set; }
public string CreatedUserName { get; set; }
public string ModifiedUserName { get; set; }
...
}
You can put all data that you need in your view model... Then, I'll recommend you create a class in your model with all the queries that you need (you have to investigate how to do), but you can get the queries from your controller (if you want).
Well, edit your controller function:
public ActionResult UserList()
{
List<userViewModel> userVM = new List<userViewModel>(); //Important! Don't return all the query, just the data that you need.
IEnumerable<user> users = null;
users = dbEntities.users.ToList();
if (users != null)
{
var userLists = (from a in users join b in users on a.created_user_id equals b.id select new { a.name, a.username, a.password, a.user_role, a.is_enable, a.is_approved, CreatedUserName = b.name, a.create_time, a.is_android, a.device_id }).ToList(); //I'm going to suppose that your query is ok and you get all the data that you need...
foreach (var item in userLists)
{
userVM.Add(new userVM(){
userVM.name = item.name;
userVM.username = item.username;
userVM.user_role = item.user_role;
.......
});
}
}
return View(userVM); //return your view model
}
Finally, modify your view and call the ViewModel userViewModel
#model Model.ViewModel.userViewModel //It depends on the namespace
//Then try something likes this...
#foreach (var aUser in Model)
{
<tr>
<td>#aUser.name</td>
<td>#aUser.username</td>
.....
<td>#Html.ActionLink("Edit", "UserEdit","Users")</td>
<td>#Html.ActionLink("Delete", "UserDelete", "Users")</td>
</tr>
}
That's the idea, improve my answer. ;)

Linq returning single item in list part of an object

I have the following database tables:
# account_type table #
id desc
----------------
1 savings
2 checking
# account table #
id account_type_id Name
--------------------------
1 2 Don
2 1 Henry
3 1 Lisa
4 2 Jenifer
I want to write a linq query so that it returns object with collection in it, i.e.
desc: Savings { Don, Jenifer }
I created these classes:
public class acctType
{
public id { get; set; }
public string desc { get; set; }
public IEnumerable<account> account{ get; set;}
}
public class account
{
public int id { get; set; }
public int account_type_id { get; set; }
public string name { get; set;}
}
My method in api is calling:
public accType Get(int id)
{
var accounts = (from c in db.account_type
where c.p_id == id
select new accType
{
id = c.id,
name = c.desc,
account = new List<account> { name = c.desc }
});
}
return accounts.FirstOrDefault()
Problem is when I get the account object back it only has single for name i.e
dsc: checking { Don }
While answer should be Don and Jenifer. How do I fix this?
You are returning only FirstOrDefault() item,Change the return statement to
return accounts.ToList()
Edit:
public List<accType> Get(int id)
{
var accounts = (from c in db.account_type
where c.p_id == id
select new accType
{
id = c.id,
name = c.desc,
account = new List<account>()
{
name=c.desc
}
});
return accounts.ToList()
}

Complex graphing in entity framework

I can't seem to find the correct where clause to get only the items I need.
I have Divisions, these contain Categories en these contain Items.
These are the classes:
public class Division {
public string Description { get; set; }
public List<Category> Categories { get; set; }
}
public class Category : IdEntity
{
public string Description { get; set; }
public Guid DivisionId { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public Guid CategoryId { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
}
What I need is a division with the Id in a parameter, all the categories from this division and the items that have a certain date for each category.
So right now I do this:
public Division GetFullDivisionByIdAndDate(Guid id, DateTime date)
{
using (new ChangeTrackingScope(ChangeTracking.Disabled))
{
var divisionGraph = new Graph<Division>().Include(d => d.Categories.Select(c => c.Items));
var division = _divisionDL.GetFullDivisionByIdAndDate(id, divisionGraph, date);
return division;
}
}
And than in the DL I do
public Division GetFullDivisionByIdAndDate(Guid id, Graph<Division> graph, DateTime date)
{
using (var db = new ContextScope<DatabaseContext>())
{
var q = graph.ApplySetReferences(db.Context.Divisions).AsNoTracking();
return q.SingleOrDefault(p => p.Id == id);
}
}
Here I get the division with all its categories (so far so good) but I also get all items and I need only the items with the date given as parameter. Anyone has an idea how to do this?
Your code is not very accessible because of a few missing methods (Graph, ApplySetReferences) so I can't hook into it. But I can show a common way to query an object graph, which is by navigation properties. In you model, a basic query body could look like this:
from d in Divisions
from c in d.Categories
from i in c.Items
select new { Div = d.Description, Cat = c.Description, Item = i.Description }
Starting from here you can add other filters and properties, like
from d in Divisions.Where(div => div.Id == id)
from c in d.Categories
from i in c.Items.Where(item => item.Date == date)
select new { ... }
Or if you want a result with items in a filtered collection:
from d in Divisions.Where(div => div.Id == id)
from c in d.Categories
select new new { Div = d.Description,
Cat = c.Description,
Items = c.Items.Where(item => item.Date == date)
}

Categories