I'm a bit new to linq and I'm unsure if what I desire is possible. I basically have a class called User that contains a bunch of attributes. I want to only fill in the Name and ID fields that I'm getting from a query. My query gives me the appropriate results, but I am unsure of how to get it into user. This is the closest I get, but I realize it's incorrect.
IEnumerable<User> UserList;
UserList = (from o in dbContext.application_user
join p in dbContext.project on application_user.user_id = project.project_manager
select o);
This returns a list of User_ID, Firstname, and Lastname, but it doesn't fit into user. My user class has multiple variables, so I was thinking of approaching it by calling 3 IEnumerables, of types int, string, and string, if I could somehow fill all 3 from one query, and then set User = new User(name = x, id = x) etc. Such as
FNameList, LNameList, ID = *insert query here*
You can construct the User object inside the Linq query:
IEnumerable<User> UserList;
UserList = (from o in dbContext.application_user
join p in dbContext.project on application_user.user_id = project.project_manager
select new User(o.FName, o.LName, o.ID));
This assumes that User has a constructor taking three arguments like those, and that the properties on o are FName, LName, and ID, but you get the idea. The select clause can be used to transform the results into whatever format you need.
You can just create your instances of User with an Select transformation.
var userQuery = from o in dbContext.application_user
join p in dbContext.project on application_user.user_id = project.project_manager
select o;
IEnumerable<User> UserList = from o in userQuery.ToList()
select new User() {Name = o.Lastname, ID = o.user_ID};
I don't know how your User class looks like, but you get the idea.
Related
I have 2 tables in a database, and one class in C# (Letter).
Tables: tblUser(Id, Name), tblLetter(Id, UserId, Title)
Letter: (Id, Title, UserName)
I want select data with linq, and use this code:
List<Letter> lst = new List<Letter>();
lst = (from l in l.tblLetter.ToList()
select new {l.Id, l.Title, UserName = l.tblUser.Name}
).ToList();
and:
List<Letter> lst = new List<Letter>
(from l in l.tblLetter.ToList()
select new {l.Id, l.Title, UserName = l.tblUser.Name});
but get this error:
Cannot implicitly convert type...
As #SnowYetis comments, you are actually selecting an instance of a new, anonymous type in your code. That's what the syntax new { ... } does. Notice that there's no type name after the new directive.
If your Letter type has the properties Id, Title, UserName then all you need to do is change new { ... } to new Letter { ... }.
If not, then we probably need more information than you're giving us—for example, the definition of the Letter type.
There are a few issues in your code:
l.tblLetter.ToList() returns all records from your table. You typically don't call ToList() until the end of your query, to get just the data you need and no more.
You want to do a join between the two tables to get the matching user name.
If you want to return a collection of Letter, you can create instances of that in your select statement instead of creating an anonymous type.
Try this:
var lst = (from l in l.tblLetter
join u in tblUser on l.UserId equals u.Id
select new Letter
{
Id = l.Id,
Title = l.Title,
UserName = u.Name
}).ToList();
The columns names and types are identical, however it's coming from two separate entities. Here is a simplified example:
--Query View
var v_res = from s in db.V_CMUCUSTOMER
select s;
--Values from table less values from view
var res = from s in db.CMUCUSTOMERs
where !(from v in v_res
select v.ID).Contains(s.ID)
select s;
--join table and view values into one variable
var res_v_res = (from c in res
select c).Union(from v in v_res
select v);
I get the following error however:
Instance argument: cannot convert from 'System.Linq.IQueryable' to System.Linq.ParallelQuery
If you specify a new anonymous type and use ToList() for both then you should be able to Union them as follows :
var v_res = (from s in db.V_CMUCUSTOMER
select new { custName = s.customer_name custAddress = s.address}).ToList();
--Values from table less values from view
var res = (from s in db.CMUCUSTOMERs
where !(from v in v_res
select v.ID).Contains(s.ID)
select new { custName = s.customer_name custAddress = s.address }).ToList();
--join table and view values into one variable
var res_v_res = v_res.Union(res);
This may be onerous if there are dozens of columns but should still work.
When I run a similar query in LINQPad, I get an error message claiming that s in Contains(s.ID) is not defined in this context.
If I replace && with where all queries are successfully executed.
In sql server, I have two tables (lets say Person and School). I want to return the Person.Name from one table and School.Name (since they are related I want the primary key of the Person also).
So the question here is can I return these data and make them all in a list? also for the return type, does it request that I create a custom type for it (I mean class)?
Edit: Sorry I forget to metion that I need it in LINQ.
Thanks,
Maybe there is a better way to do this, but I would create a new class and return a list of these objects. Like this:
First do a LiNQ query to get the values we need:
var personWithSchoolName = select p from db.Person
select s from db.School
where p.SchoolId == s.SchoolId
select new { p.PersonId as PersonId, p.Name as PersonName, s.Name as SchoolName };
peopleWithSchoolNamesList = personWithSchoolName.ToList();
Then loop through the results and add these to a new list:
foreach(object o in peopleWithSchoolNamesList)
{
PersonWithSchoolNameObject personWithSchoolNameObject = new PersonWithSchoolNameObject(o.PersonId, o.PersonName, o.SchoolName);
ListWithPeople.Add(PersonWithSchoolNameObject);
}
return ListWithPeople;
You can use Join operation of LINQ to combine data from more than one table
For return purposes you will need a Concrete class. You cannot return the results of LINQ query i.e. an anonymous type, directly.
Try this
SELECT Person.Id, Person.Name, School.Name AS SchoolName
from Person
LEFT JOIN School ON Person.SchoolId = School.Id
(actually you have to use well prepared linq to return this, yeah?)
and wrap the results into special predefined class like:
class PersonWithSchool
{
public int PersonId;
public string PersonName;
public string SchoolName;
}
Let's say I have a table of Orders and a table of Payments.
Each Payment relates to a given order: Payment.orderId
I want to query my orders:
var query = from o in db.Orders where o.blah = blah select o;
But I also need the total paid for each order:
var query = from o in db.Orders
where o.blah = blah
select new
{
Order = o,
totalPaid = (from p in db.Payments
where p.orderId == o.id
select p.Amount).Sum()
};
LINQ to SQL generates exactly the SQL query I want.
My problem is that I'm adding payments support to an existing app. Therefore, to minimize code impact, I'd like totalPaid to be a property of my Order class.
I thought of adding a "manual" property and tried to fill it at query time.
But writing the select clause is where I'm stuck:
var query = from o in db.Orders
where o.blah = blah
select "o with o.totalPaid = (from p in db.Payments <snip>).Sum()"
How can I do that?
Normally, your Order class will have a navigation property called Payments. Utilizing this, your TotalPaid property would look like this:
public double TotalPaid
{
get { return Payments.Sum(x => x.Amount); }
}
This solves another problem: This property is always up-to-date. Your approach would be outdated as soon as a new payment is added to the order.
If being up-to-date isn't so important but reducing the number of round trips to the database, you can use this code:
private double? _totalPaid;
public double TotalPaid
{
get
{
if(!_totalPaid.HasValue)
_totalPaid = Payments.Sum(x => x.Amount);
return _totalPaid.Value;
}
}
You can add a payments EntitySet int the Orders class that points to the Payments class, as well as declare the TotalPaid property as suggested from Daniel Hilgarth.
But when you query the database for the Orders, LinqToSql will make 1 query for each order, in order to get the sum of payments. The workaround is to use the DataContext.LoadWith() method like this:
DataContext db = new Datacontext(connStr);
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Orders>(o=>o.Payments);
db.LoadOptions = dlo;
var query = from o in db.Orders
where o.blah = blah
//this will load the payments with the orders in a single query
When you query existing linq results, it's like they're stuck a layer deeper than the original result. Let me explain what I mean by this.
In the example below, after getting ResultSorted, to get to the data therein, you have to use RowSorted.All.TableData.Field, but in the unsorted Result, you could just do Row.TableData.Field. In the sorted data, you have to use .All to get to the rest of the data, which is like an extra layer to get to the data you're looking for.
How can I get it so I can query Result without getting this extra layer? Thanks Stack-O!
var Result =
from a in Db.Table
select new {TableData = a};
var ResultSorted =
from a in Result
orderby a.TableData.Field
select new {All = a};
foreach(var RowSorted in ResultSorted)
{
MessageBox.Show(RowSorted.All.TableData.ToString());
}
You can use
var Result =
from a in Db.Table
select a;
var ResultSorted =
from a in Result
orderby a.Field
select a;
foreach(var RowSorted in ResultSorted)
{
MessageBox.Show(RowSorted.ToString());
}
Edit:
The thing is that
select new {TableData = a};
creates a new anonymous type with a field called TableData, like this
class Tmp1
{
TableType TableData {get; set;}
}
and
select new {All = a};
creates a new anonymous type with a field called TableData, like this
class Tmp2
{
Tmp1 All {get; set;}
}
Edit 2:
If you select a directly you don't create the extra anonymous type, instead you return the TableType.
You are returning a new instance of an anonymous type in each of your LINQ queries:
select new {TableData = a};
select new {All = a};
What you are saying to the compiler is (in the first LINQ query), "Give me a new instance of an anoymous type. I want this anonymous type to have one property named TableData and I want the value for that property to be a."
If you simply return a instead of an anoymous type, you shouldn't need to go through the properties of the nested types to get the data. Try this:
var Result =
from a in Db.Table
select a;
var ResultSorted =
from a in Result
orderby a.TableData.Field
select a;
foreach(var RowSorted in ResultSorted)
{
MessageBox.Show(RowSorted.ToString());
}
var ResultSorted =
from a in Db.Table
orderby a.Field
select a.ToString();
Edit: Fixed, didn't see the first query. This should be identical now. There is no need to create anonymous objects all the time.