join using lambda expressions and retrieving the data - c#

I have a members table with columns
member_Id,
member_Lastname,
member_Firstname,
member_Postcode,
member_Reference,
member_CardNum,
and i have another table mshipoptions with columns
mshipoption_id
mshiptype_id
and i have another table mshiptypes
mshiptype_id
mshiptype_name
another table memtomship
memtomship_id
mshipoption_id
member_id
and my entity name is eclipse
at the form load i am filling the datagrid view by using the below method....
private void reportmembers()
{
MemberControlHelper.Fillmembershiptypes(cbGEMembershiptype);
var membersreport = from tsgentity in eclipse.members
join memtomships in eclipse.membertomships on tsgentity.member_Id equals memtomships.member_Id
join mshipoptiions in eclipse.mshipoptions on memtomships.mshipOption_Id equals mshipoptiions.mshipOption_Id
join mshiptypes in eclipse.mshiptypes on mshipoptiions.mshipType_Id equals mshiptypes.mshipType_Id
select
new {
tsgentity.member_Id,
tsgentity.member_Lastname,
tsgentity.member_Firstname,
tsgentity.member_Postcode,
tsgentity.member_Reference,
tsgentity.member_CardNum,
mshiptypes.mshipType_Name,
};
if (txtfirstname.Text != "")
{
dgvmembersrep.DataSource = membersreport.Where(t => t.member_Firstname == txtlastname.Text).ToList();
}
if (txtcardnum.Text != "")
{
dgvmembersrep.DataSource = membersreport.Where(a => a.member_CardNum == txtcardnum.Text).ToList();
}
}
that was fine,...
My problem is here , i have one comboboxsay (cbgemembershiptype)......
when ever the user select the membership type in (cbgemembershiptype) i want retrieve the details of members those who have that membership type....

(Your question is unclear in terms of whether the query you've got already does what you want. I'm assuming it does.)
It's unclear why you'd want to convert this query into one using lambda expressions. It's certainly possible, but each join would introduce a new range variable - by the end, the strictly literal translation would end up with a select involving something like
member_Id = a.b.c.member_Id
... it wouldn't be very readable at all.
There are ways of improving it if you're writing it out by hand, but it still wouldn't be as clear as the query expression.
You should definitely know both forms, and write using whichever form is clearest for the query in question - which in this case is definitely the query expression form.
For more information about how query expressions are translated, see my Edulinq post on that topic.

Related

Multiple joins with multiple on statements using Linq Lambda expressions [duplicate]

Suppose I have a list of {City, State}. It originally came from the database, and I have LocationID, but by now I loaded it into memory. Suppose I also have a table of fast food restaurants that has City and State as part of the record. I need to get a list of establishments that match city and state.
NOTE: I try to describe a simplified scenario; my business domain is completely different.
I came up with the following LINQ solution:
var establishments = from r in restaurants
from l in locations
where l.LocationId == id &&
l.City == r.City &&
l.State == r.State
select r
and I feel there must be something better. For starters, I already have City/State in memory - so to go back to the database only to have a join seems very inefficient. I am looking for some way to say {r.City, r.State} match Any(MyList) where MyList is my collection of City/State.
UPDATE
I tried to update based on suggestion below:
List<CityState> myCityStates = ...;
var establishments =
from r in restaurants
join l in myCityStates
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
and I got the following compile error:
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
UPDATE 2
Compiler didn't like anonymous class in the join. I made it explicit and it stopped complaining. I'll see if it actually works in the morning...
It seems to me that you need this:
var establishments =
from r in restaurants
join l in locations.Where(x => x.LocationId == id)
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
Well, there isn't a lot more that you can do, as long as you rely on a table lookup, the only thing you can do to speed up things is to put an index on City and State.
The linq statement has to translate into a valid SQL Statement, where "Any" would translate to something like :
SELECT * FROM Restaurants where City in ('...all cities')
I dont know if other ORM's give better performance for these types of scenarios that EF, but it might be worth investigating. EF has never had a rumor for being fast on reads.
Edit: You can also do this:
List<string> names = new List { "John", "Max", "Pete" };
bool has = customers.Any(cus => names.Contains(cus.FirstName));
this will produce the necessary IN('value1', 'value2' ...) functionality that you were looking for

Linq to get data from a table but not if in another table?

Because of a poor design on our database I have to write a complex query to get my data.
I need to get all valid data from a table_1. In other works I need to get each valid row of my table_1. I don't have a simple valid or invalid column in my table_1. This information is stored in a table_2. Table_2 contains all invalid row with the error message.
Let say the data I need to retrieve are orders. For my example notice that OrderNo is the number of an order. So I can have multiple line, version, of this order in table_1 and I can also have multiple line of error on this order in table_2. So I will also have to use a version number.
I already tried this:
table_1.Where(y => (y.OrderNo == "1234"));
table_2.Where(y => (y.OrderNo == "1234")).Select(y => y.Version).Distinct();
And I think I need to do something like this:
var errorList = table_2.Where(y => (y.OrderNo == "1234")).Select(y => y.Version).Distinct();
table_1.Where(y => (y.OrderNo == "1234" && y.Version.NOT_IN(erriList)));
Could you help me?
I suppose you are searching for Contains function with ! symbol (logical negation operator). Like this:
var errorList = table_2.Where(y => y.OrderNo == "1234")
.Select(y => y.Version);
var res = table_1.Where(y => y.OrderNo == "1234"
//here you get only rows that doesn't represent in errorList
&& !errorList.Contains(y.Version));
to get data from a table but not if in another table
This is called antijoin. While you can use Contains and Any based approaches presented in the other answers, usually you'll get the best performance by using the classic SQL approach - LEFT OUTER JOIN combined with checking the right side for NULL.
Which in LINQ looks like this:
var query =
from t1 in table_1
//where t1.OrderNo == "1234"
join t2 in table_2 on t1.OrderNo equals t2.OrderNo into t2group
from t2 in t2group.DefaultIfEmpty()
where t2 == null
select t1;
Actually when you use OrderNo filter, most probably there will not be a noticeable speed difference between this and other queries. The main benefit of the above would be if you remove that filter, although many nowadays SQL query optimizers are able to derive one and the same execution plan regardless of whether the query uses JOIN / IN / EXISTS constructs.
How about this:
var validRows = table1
.Where(t1 => !table2
.Any(t2 => t1.OrderNo == t2.OrderNo &&
t1.Version == t2.Version));
Note that this is far more efficient in SQL unless you're using something fancy that translates the expression to SQL.

LINQ to SQL join generates SQL which joins on IS NULL

I am not good at Linq expression, today I am running into one weird issue as the below of inner join statement,
var orders = (from q in dao.CurrentDBContext.New_OrderForm
join d in dao.CurrentDBContext.New_OrderGoodsDetail on q.billNum equals d.billNum
select new
{
q.billNum,
q.orderSource,
q.sourceOddNum
d.PPT
}
While I traced the linq statement, I am confused of that Entity Framework will convert the linq statement to the below sql statment
SELECT
[Extent1].[billNum] AS [billNum],
[Extent1].[orderSource] AS [orderSource],
[Extent1].[sourceOddNum] AS [sourceOddNum],
[Extent2].[PPT] AS [PPT]
FROM [dbo].[New_OrderForm] AS [Extent1]
INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2]
ON ([Extent1].[billNum] = [Extent2].[billNum]) OR
(([Extent1].[billNum] IS NULL) AND ([Extent2].[billNum] IS NULL))
Do you know why the below SQL segment did automatically append?
OR (([Extent1].[billNum] IS NULL) AND ([Extent2].[billNum] IS NULL)"
I don't expect that the above automatically append, since it did slow down SQL performance. Any suggestions?
Here is what you can do in case you cannot change the billNum columns to be non nullable.
First, set the option mentioned by #Giorgi
class CurrentDBContext
{
public CurrentDBContext()
{
Configuration.UseDatabaseNullSemantics = true;
// ...
}
}
Then change the LINQ query to not use join, but simple where like this
var orders = (from q in dao.CurrentDBContext.New_OrderForm
from d in dao.CurrentDBContext.New_OrderGoodsDetail
where q.billNum == d.billNum
select ...
The result will be the exact SQL query as the one you've shown (with JOIN!) without the OR part.
It seems that Linq translates q.billNum equals d.billNum is such a way that it also includes a valid match in case both q.billNum and d.billNum are NULL (in SQL NULL is never equal to NULL, hence the OR in your query).
Making both fields non-nullable would be the best solution, provided both fields can never be NULL.
If this is not the case, you could also try to add a where clause in your Linq statement to specifiy that both q.billNum and d.billNum cannot be NULL. With any luck, Linq will recognize that nullable values are not possible.
Note: If you are working with Oracle you should check for empty strings as well as NULL (empty string is equivalent to NULL). Empty strings should be fine as a valid value in SQL Server.
As the above did not help, you could try to write the query yourself. If I'm not mistaking it would be something along the following lines (assuming var is an List<Order> in your example code - the results of your query should match the class you are using):
StringBuilder query = new StringBuilder();
query.AppendLine("SELECT [Extent1].[billNum] AS [billNum],");
query.AppendLine(" [Extent1].[orderSource] AS [orderSource],");
query.AppendLine(" [Extent1].[sourceOddNum] AS [sourceOddNum],");
query.AppendLine(" [Extent2].[PPT] AS [PPT]");
query.AppendLine("FROM [dbo].[New_OrderForm] AS [Extent1]");
query.AppendLine("INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2] ON [Extent1].[billNum] = [Extent2].[billNum]");
List<Order> orders = DbContext.Database.SqlQuery<Order>(query.ToString()).ToList();
I have used similar workarounds to get around performance issues in the past.
If you are using EF6 try setting
context.Configuration.UseDatabaseNullSemantics = true;
and it will not generate NULL checks for those columns.
According to documentation
For example (operand1 == operand2) will be translated as: (operand1 = operand2) if UseDatabaseNullSemantics is true, respectively (((operand1 = operand2) AND (NOT (operand1 IS NULL OR operand2 IS NULL))) OR ((operand1 IS NULL) AND (operand2 IS NULL))) if UseDatabaseNullSemantics is false.
Following on from #Giorgi's answer, the UseDatabaseNullSemantics flag will not work with the equals keyword - only the == operand. Thus in order to get round this and ensure the join on billNum is not part of the OR clause this approach should work (in conjunction with the UseDatabaseNullSemantics flag):
var orders = (from q in dao.CurrentDBContext.New_OrderForm
from d in dao.CurrentDBContext.New_OrderGoodsDetail
where q.billNum == d.billNum
select new
{
q.billNum,
q.orderSource,
q.sourceOddNum
d.PPT
}
This will generate the JOIN without the OR.

Join vs Navigation property for sub lists in Entity Framework

I have a sql statement like this:
DECLARE #destinations table(destinationId int)
INSERT INTO #destinations
VALUES (414),(416)
SELECT *
FROM GroupOrder grp (NOLOCK)
JOIN DestinationGroupItem destItem (NOLOCK)
ON destItem.GroupOrderId = grp.GroupOrderId
JOIN #destinations dests
ON destItem.DestinationId = dests.destinationId
WHERE OrderId = 5662
I am using entity framework and I am having a hard time getting this query into Linq. (The only reason I wrote the query above was to help me conceptualize what I was looking for.)
I have an IQueryable of GroupOrder entities and a List of integers that are my destinations.
After looking at this I realize that I can probably just do two joins (like my SQL query) and get to what I want.
But it seems a bit odd to do that because a GroupOrder object already has a list of DestinationGroupItem objects on it.
I am a bit confused how to use the Navigation property on the GroupOrder when I have an IQueryable listing of GroupOrders.
Also, if possible, I would like to do this in one trip to the database. (I think I could do a few foreach loops to get this done, but it would not be as efficient as a single IQueryable run to the database.)
NOTE: I prefer fluent linq syntax over the query linq syntax. But beggars can't be choosers so I will take whatever I can get.
If you already have the DestinationGroupItem as a Navigation-property, then you already have your SQL-JOIN equivalent - example. Load the related entities with Include. Use List's Contains extension method to see if the desired DestinationId(s) is(are) hit:
var destinations = new List<int> { 414, 416 };
var query = from order in GroupOrder.Include(o => o.DestinationGroupItem) // this is the join via the navigation property
where order.OrderId == 5662 && destinations.Contain(order.DestinationGroupItem.DestinationId)
select order;
// OR
var query = dataContext.GroupOrder
.Include(o => o.DestinationGroupItem)
.Where(order => order.OrderId == 5662 && destinations.Contain(order.DestinationGroupItem.DestinationId));

linq to sql report tables in query

Here's the method i want to write:
public static IEnumerable<String> GetTableNames(this IQueryable<T> query)
{
//...
}
where the IQueryable is a linq-to-sql query (is there a more specific interface i should use?).
then if i had a query like this
var q = from c in db.Customers
from p in db.Products
from cp in db.CustomerProducts
where c.ID = 3 && cp.CustID == c.ID && p.ID == cp.ProdID
select new {p.Name, p.Version};
q.GetTableNames();// return ["Customers", "Products", "CustomerProducts"]
basically it would show all the tables that this query touches in the db, it is ok to execute the query to figure this out too (since that is going to happen anyway)? any ideas?
(EDIT: sorry if this is a little too "give me teh codez!", any tips, partial solutions, or explanations of why this is impossible will be considered Thanks! -Luke)
LINQ-to-SQL allows you to capture the TSQL easily - just set .Log. The problem then is parsing that. I would be tempted to capture the TSQL, and play it back in SSMS with SET STATISTICS IO ON - this gives easily parsed per-table results.
You could do DataContext.GetCommand(query).CommandText, then parse that looking for FROM and JOIN clauses (tricky would be cartesian (comma) joins).

Categories

Resources