Comparing nullable and non-nullable properties on a join (Linq) [duplicate] - c#

I am working on a WinForms project which requires to use Linq-To-Sql. I have been able to create my DataContext using the SqlMetal tool, and make some queries. But right now I have a problem that I havent been able to solve.
I am trying to make a LEFT OUTER JOIN as following:
MyDatabase db = new MyDatabase(...);
var query = from p in db.ParentTable
join t in db.ChildTable on new {A = p.child_ID, B = p.OtherID}
equals new {A = t.ID, B = t.OtherID} into j1
from c in j1.DefaultIfEmpty()
select new
{
...
};
When I write this query an error is raised at compile-time in the join word:
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'GroupJoin'
I know this error is caused by the comparison between p.child_ID and t.ID since p.child_ID is int? and t.ID is int. But, How can I solve this? How can I perform the LEFT OUTER JOIN without having this error??
p.child_ID is int? since this column is marked as IS NULL in SQL.
Hope someone can help me, thanks in advance.

You can use GetValueOrDefault() method. It retrieves the value of the current Nullable object, or the object's default value.
In terms of your example :
var query = from p in db.ParentTable
join t in db.ChildTable on new {A = p.child_ID.GetValueOrDefault(0), B = p.OtherID}
equals new {A = t.ID, B = t.OtherID} into j1
from c in j1.DefaultIfEmpty()
select new
{
...
};
If the p.child_ID become null than it will return 0. Hope this will help !!

The first problem was that the property names should be the same as said casperOne♦, but the second problem is that you're comparing a nullable-int, p.child_ID, with a non-nullable-int, t.ID. So you could use the null-coalescing operator in this way:
(int)(p.child_ID ?? default(int))
In this case returns p.child_ID if it isn't null else returns default(int), that is 0.
The query will be:
var query = from p in db.ParentTable
join t in db.ChildTable on new {A = (int)(p.child_ID ?? default(T)), B = p.OtherID}
equals new {A = t.ID, B = t.OtherID} into j1
from c in j1.DefaultIfEmpty()
select new
{
//...
};

Related

How to translate SQL to LINQ with individual field joins?

I have a SQL statement, that I want to implement in a .NET website project. I don't understand how to join in a particular field, as the fields aren't available when I just select from the table.
My SQL
SELECT *
FROM [vResidence] c
JOIN [vJobs] j on c.UID = j.UID
This is the LINQ I have tried, but I am stuck at the 'ON' part:
results = (from j in vJobs
join cr in vResidence on ??? )
When I try 'j.', the only option I get is 'equals'.
You can follow as this.connect to tables as JOIN use equals keyword
var result = from r in vResidence
join j vJobs on r.UID equals j.UID
select new {[yourcolnum]};
You can try this
var result = (from j in vJobs
join cr in vResidence
on j.UID equals cr.UID
select new {
...
}).ToList();
The Linq expression is the following:
from t1 in Table1
join t2 in Table2
on t1.ID equals t2.ID
The join clause on must be do in order: first the first table, then the second.
The keyword equals must be use.
Apart from the above Linq answers, we can do JOIN using Enumerable.Join extension with Lambda expressions. Try something like,
var result = vJobs.Join(vResidence, jb => new { jb.UID }, res => new { res.UID },
(jb, res) => new { jb, res })
.Select(x => x.jb) //Select the required properties (from both objects) with anonymous object or select left/right object
.ToList();
C# Fiddle with sample data.

Linq multiple join conditions using extra variables

I have this query:
SELECT *
FROM transaction t
JOIN transactionDetail toTrans ON t.id = toTrans.tId and toTrans.FlowDirection= 1
JOIN transactionDetail fromTrans ON t.id = fromTrans.tId and fromTrans.FlowDirection= 0
Which I tried to recreate using anonymous types, as explained here.
byte toFlow = 1;
byte fromFlow = 1;
from trans in data.Transactions
join toTrans in data.TransactionDetails on new {trans.id, toFlow} equals new {toTrans.tId, toTrans.FlowDirection}
join fromTrans in data.TransactionDetails on new { trans.id, fromFlow } equals new { fromTrans.tId, fromTrans.FlowDirection }
Flowdirection is always either 1 or 0, so I'm using the toFlow byte. This however, gives the error:
The type of one of the expressions in the join clause is incorrect.
According to this answer, both name and types need to match. Which would mean:
byte FlowDirection= 1;
from trans in data.Transactions
join toTrans in data.TransactionDetails on new {trans.id, FlowDirection} equals new {toTrans.tId, toTrans.FlowDirection}
join fromTrans in data.TransactionDetails on new { trans.id, FlowDirection} equals new { fromTrans.tId, fromTrans.FlowDirection }
Which works! However, the second join needs to have a FlowDirection of value 0 instead of 1. How can I change the value of FlowDirection? I can't change the name of the variable or subtract 1 inside the anonymous object, or else this would have been easy.
Just to expand on the commentary:
Surely you can just use two constants (or literals)?, i.e.
from trans in data.Transactions
join toTrans in data.TransactionDetails
on new {ID = trans.id, Flow = (byte)1}
equals new {Id = toTrans.tId, Flow = toTrans.FlowDirection}
join fromTrans in data.TransactionDetails
on new { Id = trans.id, Flow = (byte)0}
equals new { Id = fromTrans.tId, Flow = fromTrans.FlowDirection }
Could FlowDirect - 1 not work because it turns FlowDirect into an int instead? Does subtracting an int from a byte turn the byte into an int maybe? Otherwise, I really don't know why your code works.
Yes, you would need to cast the result back to byte (or the literal 1 to byte so that byte operator "-" is used)
How can I change the value of FlowDirection? I can't change the name of the variable or subtract 1 inside the anonymous object
To change your variable inside the anonymous object simply do:
new { fromTrans.tId, FlowDirection = fromTrans.FlowDirection - 1 }
for example.
Another idea:
from trans in data.Transactions
join toTrans in data.TransactionDetails on trans.id equals new toTrans.tId
join fromTrans in data.TransactionDetails on trans.id equals fromTrans.tId
where toTrans.FlowDirection == 1 && fromTrans.FlowDirection == 1
I think this option should be easier to read.

Non-Nullable value type issue when LEFT JOIN in Linq

I am going to work on a WinForms project which requires to use Linq, but I know nothing about it, so I just started learning.
After reading a tutorial about Linq I created the System.Data.Linq.DataContext using the SqlMetal tool which comes with the Microsoft Windows SDK. The database Im using as example is "Northwind".
I have been able to make simple queries as following:
Northwind db = new Northwind("Data Source=local;Initial Catalog=Northwind;Integrated Security=True");
var query = from c in db.Customers
join o in db.Orders on c.CustomerID equals o.CustomerID
select new
{
Customer_ID = c.CustomerID,
Customer_Name = c.ContactName,
Order_ID = o.OrderID
};
And then I show the results in a DataGridView and everithing goes well.
But right now I am facing a problem which I havent been able to solve.
I tried to modify the previous query in order to make a LEFT OUTER JOIN, so what I did was the following:
var query = from c in db.Customers
join o in db.Orders on c.CustomerID equals o.CustomerID into j
from or in j.DefaultIfEmpty()
select new
{
Customer_ID = c.CustomerID,
Customer_Name = c.ContactName,
Order_ID = or.OrderID
};
But, when I try to show these set of data in a DataGridView an error is raised:
The null value cannot be assigned to a member with type System.Int32 which is a non-nullable value type.
I know this problem is caused by the "Customers" that dont have a "Order", so the system tries to asign a null value to a int variable.
But then, How can I make a LEFT OUTER JOIN without having this error?
Thank you in advance.
You should not convert the type to a string. Simple cast it to the equivalent nullable type:
var query = from c in db.Customers
join o in db.Orders on c.CustomerID equals o.CustomerID into j1
from or in j1.DefaultIfEmpty()
select new
{
Customer_ID = c.CustomerID,
Customer_Name = c.ContactName,
Order_ID = (int?)or.OrderID
};
I think I just found the answer:
var query = from c in db.Customers
join o in db.Orders on c.CustomerID equals o.CustomerID into j1
from or in j1.DefaultIfEmpty()
select new
{
Customer_ID = c.CustomerID,
Customer_Name = c.ContactName,
Order_ID = or.OrderID == null ? "[null]" : or.OrderID.ToString()
};
but Id appreciate if someone has a better answer

LinQ query with multiple tables and extracting data

I'm doing a query to inner join 4 tables and I have to extract data and convert into string and place it in an array for it.
var query = from a in context.as
join b in context.bs on a.prikey equals b.forkey
join c in context.cs on b.prikey equals c.forkey
join d in context.ds on c.prikey equals d.forkey
where b.gender == gender
where c.age == age
select new
{
a.Name,
a.Age,
b.Gender,
};
string[] results = new string[] {}
return results;
Normally, if a single table is involved
a as = plural of table a
as t = query.First()
string[] results = new string[] {t.Name, t.Age, t.Gender}
return results;
I'm missing a step to extract the data.
It depends what exactly you want to do with the data. Your code won't actually compile at the moment as it's trying to create an anonymous type with multiple properties all called "arg" but I'm assuming you've really got a more sensible query.
Ultimately, the fact that you're using multiple tables is irrelevant here - you're only getting a single result element at a time: the fact that each result element contains data from multiple tables is neither here nor there in terms of how you access it.
Now I've just noticed that you say you want to "extract data and convert into string". If possible, you should express that in your query. You may be able to do that at the database, or you may need to force the final part of the execution to execute locally, like this:
// Not executed yet!
var dbQuery = from a in context.a
join b in context.bs on a.prikey equals b.forkey
join c in context.cs on b.prikey equals c.forkey
join d in context.ds on c.prikey equals d.forkey
where ...
select { a.Age, b.Name, c.Salary, d.Location };
// This still won't talk to the database!
var finalQuery = dbQuery.AsEnumerable()
.Select(x => string.format("Age: {0}; Name: {1}; " +
"Salary: {2}; Location: {3}",
x.Age, x.Name, x.Salary,
x.Location));
// This will finally execute the query
string[] results = finalQuery.ToArray();
Now you don't have to do it like this - but it's probably the best approach, at least with the amount of information you've given us. If you can tell us more about how you're trying to combine the data from the multiple tables, we may be able to help you more.
EDIT: Okay, now you've given us a bit more information, I suspect you want:
var query = from a in context.a
join b in context.bs on a.prikey equals b.forkey
join c in context.cs on b.prikey equals c.forkey
join d in context.ds on c.prikey equals d.forkey
where ...
select new string[] { a.arg, b.arg, c.arg, d.arg };
string[] results = query.First();
I haven't tried creating arrays in LINQ to SQL... that may work, or you may need to go via an anonymous type and AsEnumerable as per the earlier part of my answer.
You should also think about what you want to happen if there are no results, or multiple results.
EDIT: Having seen the edited question, you really can treat multiple tables the same way as a single table. You'd use exactly the same code for handling the result, once it's been projected into an anonymous type:
var query = from a in context.as
join b in context.bs on a.prikey equals b.forkey
join c in context.cs on b.prikey equals c.forkey
join d in context.ds on c.prikey equals d.forkey
where ...
select new { a.Name, a.Age, b.Gender };
var result = query.First();
// Call ToString appropriately on each property, of course
string[] array = new string[] { result.Name, result.Age, result.Gender };
var query = from a in context.a
join b in context.bs on a.prikey equals b.forkey
join c in context.cs on b.prikey equals c.forkey
join d in context.ds on c.prikey equals d.forkey
where a.arg == arg
where b.arg == arg
where c.arg == arg
select new
{
allStrings = a.arg +
a.arg +
b.arg +
c.arg +
d.arg
};
string[] results = query.ToArray();

error with linq join

I have this linq query:
var segreterie = from s in db.USR_Utenti join h in db.USR_Accounts
on new {s.ID, settings.GruppoSegreteria}
equals new {h.USR_UtentiReference,h.ID_Gruppo} select s;
that has this problem:
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
how can i do to solve it?
The two types should be the same - which means they'll need the same property names as you're using anonymous types. Try this:
var segreterie = from s in db.USR_Utenti join h in db.USR_Accounts
on new {s.ID, Groups = settings.GruppoSegreteria}
equals new {ID = h.USR_UtentiReference, Groups = h.ID_Gruppo}
select s;
That's assuming that s.ID and h.USR_UtentiReference have the same type, and settings.GruppoSegreteria and h.ID_Gruppo do likewise.
On a similar note, if you are doing a join on a field which is nullable (for e.g., int and int?) you may have to do something like (where Field2 is int? in Table1 and int in Table2):
from l in Table1
join l2 in Table2
on new { l.Field1, Field2 = (l.Field2.Value == null ? -1 : l.Field2.Value) } equals new { l2.Field1, Field2 = l2.Field2 }

Categories