Entity Framework + LINQ Expression outer join error - c#

i want to create a left outer join for a linq expression that query data from database via entity framework. this is the linq expression. basically what I am trying to do is search problem_vehicle_id from problemVehiclesTicket in Problems table to see if it exists, if it doesn't exists, i want to return a problem object that is null/empty. Basically I believe it is left outer join.
var ticketsDetails = (from tickets in DbContext.tickets
join problemVehiclesTicket in DbContext.problem_vehicle on tickets.tickets_id equals problemVehiclesTicket.tickets_id
join problems in DbContext.problem on problemVehiclesTicket.problem_vehicle_id equals problem.problem_vehicle_id into problemGroup
from problems in problemGroup.DefaultIfEmpty(new problem { })
where (tickets.tickets_id == ticketsId)
select new TicketsDetails
{
Ticket = tickets,
ProblemVehicle = problemVehiclesTicket,
Problems= problem,
}).ToList();
Problem is a class that mirrors that of the Problem table in database
`Problem`
id (int), description (string), type (short)
The error i got is "The entity or complex type 'SPOTS_Repository.speeding_offence' cannot be constructed in a LINQ to Entities query." The source is from Entity Framework.
any help is greatly appreciated.

The type problem in your case is a mapped entity. Therefore, you cannot project onto it. You can use an anonymous type or another non-mapped class (DTO).
Because in your DefaultIfEmpty method you are constructing a new problem, which is a mapped entity, this not allowed.
Fix
You do not need to pass anything to DefaultIfEmpty method. Actually in your case, you are not even allowed because the only thing you can pass is problem and that is mapped. Therefore, use .DefaultIfEmpty() without creating a new problem.
More Belabor
Here is an example, which will clarify the usage of DefaultIfEmpty:
Option 1: DefaultIfEmpty() with No Parameter
var list1 = new List<int> { 1, 2, 3, 6, 4 };
var list2 = new List<int> { 4, 1, 2 };
var selection =
from l1 in list1
join l2 in list2 on l1 equals l2 into joined
from j in joined.DefaultIfEmpty()
select j;
Output: 1, 2, 0, 0, 4
Why? Because 3 and 6 are not found and DefaultIfEmpty for an integer returns a 0.
Option 2: DefaultIfEmpty() with Parameter
In some cases we may want to indicate that if the item is not found in the join, what to return instead. We can do that by sending a single parameter to DefaultIfEmpty method like this:
var list1 = new List<int> { 1, 2, 3, 6, 4 };
var list2 = new List<int> { 4, 1, 2 };
var selection =
from l1 in list1
join l2 in list2 on l1 equals l2 into joined
from j in joined.DefaultIfEmpty(99) //<-- see this
select j;
Output: 1, 2, 99, 99, 4 Why? Because 3 and 6 are not found and we instructed DefaultIfEmpty to return a 99 in that case.
Please note that DefaultIfEmpty is a generic method. In my case it required an int because I am joining to the second list which is a List of int(s). In your case it is problem(s) but that is mapped. Therefore, you cannot construct it in your query.
Here is another example:
var depts = new List<Department>
{
new Department { Name = "Accounting" },
new Department { Name = "IT" },
new Department { Name = "Marketing" }
};
var persons = new List<Person>
{
new Person { DeptName = "Accounting", Name = "Bob" }
};
var selection2 =
from d in depts
join p in persons on d.Name equals p.DeptName into joined2
// See here DefaultIfEmpty can be passed a Person
from j2 in joined2.DefaultIfEmpty(new Person { DeptName = "Unknown", Name = "Alien" })
select j2;
foreach(var thisJ in selection2)
{
Console.WriteLine("Dept: {0}, Name: {1}", thisJ.DeptName, thisJ.Name);
}
Output:
Dept: Accounting, Name: Bob
Dept: Unknown, Name: Alien
Dept: Unknown, Name: Alien
<== Fiddle Me ==>

Public class problem()
{
public int id;
public string description;
public short type;
}
.DefaultIfEmpty(
new problem()
{
Id = ticketsId,
Description = string.empty,
});
create class and make use of that in linq query
Hope it helps you.

Related

Understanding the GroupJoin and Join in Linq chaining syntax (Homework)

I need help with understanding the fourth argument for GroupJoin. From what i understand so far GroupJoin takes 4 arguments: (1, 2) - the first one is the secondary list and argument two is a Func that returns the key from the first object type in other words from the first list in this case (people). (3, 4) A Func that returns the key from the second object type from the second list in this case (items), and one that stores the grouped object with the group itself (I can't understand the code for this part). Considering this and having the code below:
var products = new Product[]
{
new Product { Id = 1, Type = "Phone", Model = "OnePlus", Price = 1000 },
new Product { Id = 2, Type = "Phone", Model = "Apple", Price = 2000 },
new Product { Id = 3, Type = "Phone", Model = "Samsung", Price = 1500 },
new Product { Id = 4, Type = "TV", Model = "Samsung 32", Price = 200 },
};
var people = new Person[]
{
new Person { Id = 1, Name = "Ivan Ivanov", Money = 150000 },
new Person { Id = 2, Name = "Dragan Draganov", Money = 250000 },
new Person { Id = 3, Name = "Ivelin Ivelinov", Money = 350000
}
};
var items = new Item[]
{
new Item { PersonId = 1, ProductId = 1, Amount = 1 },
new Item { PersonId = 1, ProductId = 4, Amount = 1 },
new Item { PersonId = 1, ProductId = 5, Amount = 1 },
new Item { PersonId = 1, ProductId = 7, Amount = 1 },
new Item { PersonId = 2, ProductId = 2, Amount = 1 },
};
Query:
var productOwnerList = people
.GroupJoin(
items,
o => o.Id,
i => i.PersonId,
(o, i) => new <--- (**)
{
Person = o,
Products = i
.Join(products,
o1 => o1.ProductId,
i2 => i2.Id,
(o1, i2) => i2) <--- (*)
.ToArray()
})
.ToArray();
Just to mention I post only a few lines for the data. I need help to understand what the 4th argument for the join method is performing here -> (*) (stores the grouped object with the group itself) ? When i watch the result i see it it puts all Person id's associate with the product keys and joined the two lists based on Items list (one to many). But i cannot get what exactly this line means (o1, o2) => i2). Its obvious what is doing (put all the items associated with the person id in a array (items[]) for every person. but what is "under the hood" here ? Also one question about (**) this line its creating new object, is this a anonymous class or if its not what is it.
The fourth argument - which maps to the fifth parameter in the documentation (because the first parameter is the target of the extension method call) is just the result selector. It's a function accepting two parameters: the first is an element of the "outer" sequence (the people array in your case) and the second is a sequence of elements from the "inner" sequence (the items array in your case) which have the same key as the outer element. The function should return a "result" element, and the overall result of the method call is a sequence of those results.
The function is called once for each of the "outer" elements, so you'd have:
First call: person ID 1, and products with IDs 1, 4, 5, 7
Second call: person ID 2, and the product with ID 2
Third call: person ID 3, and an empty sequence of products
Your query is complex because you're using an anonymous type for your result, and constructing an instance of the anonymous type using another query. Here's a simpler query that might help to clarify:
var productOwnerList = people
.GroupJoin(
items,
o => o.Id,
i => i.PersonId,
(person, items) => $"{person.Id}: {string.Join(",", items.Select(item => item.ProductId))}"
.ToArray();

How to Convert Linq to Lambda Expression

var getr = (from d in _context.DR
join r in _context.R on d.RID equals r.RID
where HID == r.HID && cI >= d.DRD && cO < d.DRD
group d by new {d.RID, d.RGID} into g
select g);
How to convert Linq to lambda? This is what I got:
var getr = _context.DR.Join(_context.R, x => x.RID, y => y.RID, (x, y) => new { R= x, DR= y}).Where(z => z.DR.RID== y.RID);
Are there any pros and cons of using either one?
In terms of performance : there is no performance difference whatsoever between two.
Which one should use is mostly personal preference, but its important to bear in mind that there are situation where one will be better suited the other.
int[] ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// using Query expression
var evensQuery = from i in ints where isEven(i) select i;
// using Lambda expression
var evensLambda = ints.Where(isEven);
There so many function that available with lambda ie. single(), First(), Take(), Skip()..
Although you can mix and match the two by calling the Lambda-only methods at the end of the query:
// mix and match query and Lambda syntax
//Example ver :1
var query = (from person in people
join pet in pets on person equals pet.Owner
select new { OwnerName = person.Name, Pet = pet.Name }).Skip(1).Take(2);
or, for better readability :
//Example ver :2
var query = from person in people
join pet in pets on person equals pet.Owner
select new { OwnerName = person.Name, Pet = pet.Name };
var result = query.Skip(1).Take(2);
Both example version return the same output without performance differences because of delayed(or Deferred ) execution, that means query is not executing at the point of declaration, but it will execute when try to iterate through the result variable.
BUT, if you don’t want delayed execution, or need to use one of the aggregate functions such as Average() or Sum(), for example, you should be aware of the possibility of the underlying sequence being modified between the assignments to query and result. In this case,I’d argue it’s best to use Lambda expressions to start with or add the Lambda-only methods to the query expression.

Linq query to fetch DB records corresponding to List of int[2]

I am trying to count the VALUEs corresponding to a List<int[]> using Linq to Entity Framework.
I have a List<int[]> where each int[] in the List is of length 2.
I have a DB table VALUES which contains 3 columns, called ID, PARENT and VALUE, where each int[] in the List (see 1) may correspond to a record in the table, the 0 index being ID and the 1 index being PARENT. *But some arrays likely do not correspond to any existing records in the table.
Each combination of ID and PARENT correspond to multiple DB records, with different VALUEs.
Several points that are important to note:
One of the problems is that I can't rely on ID alone - each value is defined/located according to both the ID and PARENT.
None of the int arrays repeat, though the value in each index may appear in several arrays, e.g.
List<int[]> myList = new List<int[]>();
myList.add(new int[]{2, 1});
myList.add(new int[]{3, 1}); //Notice - same "PARENT"
myList.add(new int[]{4, 1}); //Notice - same "PARENT"
myList.add(new int[]{3, 1}); //!!!! Cannot exist - already in the List
I can't seem to figure out how to request all of the VALUEs from the VALUES table that correspond to the ID, PARENT pairs in the List<int[]>.
I've tried several variations but keep arriving at the pitfall of attempting to compare an array in a linq statement... I can't seem to crack it without loading substantially more information that I actually need.
Probably the closest I've gotten is with the following line:
var myList = new List<int[]>();
// ... fill the list ...
var res = myContext.VALUES.Where(v => myList.Any(listItem => listItem[0] == v.ID && listItem[1] == v.PARENT));
Of course, this can't work because The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities.
#chris huber
I tried it out but it was unsuccessful.
2 things:
Where you created "myValues" I have a DB table entity, not a List.
Due to point number 1, I am using LINQ to Entities, as opposed to LINQ to Object
My code then comes to something like this:
var q2 = from value in myContext.VALUES where myList.Select(x => new { ID = x.ElementAt(0), Parent = x.ElementAt(1) }).Contains(new { ID = value.ID, Parent = value.PARENT }) select value;
This returns the following error message when run:
LINQ to Entities does not recognize the method 'Int32 ElementAt[Int32](System.Collections.Generic.IEnumerable` 1[System.Int32],Int32)' method, and this method cannot be translated into a store expression.
#Ovidiu
I attempted your solution as well but the same problem as above:
As I am using LINQ to Entities, there are simply certain things that cannot be performed, in this case - the ToString() method is "not recognized". Removing the ToString() method and attempting to simply have a Int32 + "|" + In32 gives me a whole other error about LINQ to Entities not being able to cast an Int32 to Object.
Use the following LINQ expression:
List<int[]> myList = new List<int[]>();
myList.Add(new int[] { 2, 1 });
myList.Add(new int[] { 3, 1 }); //Notice - same "PARENT"
myList.Add(new int[] { 4, 1 }); //Notice - same "PARENT"
myList.Add(new int[] { 3, 1 });
List<int[]> myValues = new List<int[]>();
myValues.Add(new int[] { 2, 1 , 1});
myValues.Add(new int[] { 3, 1 , 2}); //Notice - same "PARENT"
myValues.Add(new int[] { 4, 1 , 3}); //Notice - same "PARENT"
myValues.Add(new int[] { 3, 1, 4 });
myValues.Add(new int[] { 3, 2, 4 });
var q2 = from value in myValues where myList.Select(x => new { ID = x.ElementAt(0), Parent = x.ElementAt(1) }).Contains(new { ID = value.ElementAt(0), Parent = value.ElementAt(1) }) select value;
var list = q2.ToList();
You could use this workaround: create a new List<string> from your List<int[]> and compare the values in your table with the new list.
I didn't test this with EF, but it might work
List<string> strList = myList.Select(x => x[0].ToString() + "|" + x[1].ToString()).ToList();
var res = myContext.VALUES.Where(x => strList.Contains(SqlFunctions.StringConvert((double)x.ID).Trim() + "|" + SqlFunctions.StringConvert((double)x.PARENT).Trim()));

Linq retrieving items that have changed between sets and items not in sets

Here are my classes:
public class XDetail
{
public string Name { get; set; }
public int ID { get; set; }
}
public class X
{
public int XID { get; set; }
public int ID { get; set; }
}
The ID is shared between them to link X and XDetail (one to many relationship) and X and XDetail are really typed DataRows. I read in a file using the following linq query and shape an anonymous type:
var results = (from line in File.ReadAllLines(file)
select new
{
XID = int.Parse(line.Substring(0, 8).TrimStart('0')),
Name = line.Substring(8, 255).Trim()
}).ToList();
This data is used to check against existing X/XDetail to make appropriate changes or add new records. I wrapping the results in a check to see if it throws on the .ToList() when the sequence has no results. XList is a List and XDetailList is a List.
From there I attempt a fancy linq query to match up the appropriate items:
var changedData = from x in XList
join xDetail in XDetailList on x.ID equals xDetail.ID
where
(!results.Any(p => p.XID.Equals(x.XID))
|| !results.Any(p => p.Name.Equals(xDetail.Name)))
select new
{
XValue = x,
XDetailValue = xDetail,
Result = (from result in results
where result.Name.Equals(xDetail.Name)
select result).SingleOrDefault()
};
My new problem is that this query will only provide me with what has changed in X/XDetail and not what is new. To accomplish getting what is new I have to run another query which seemed fine enough while testing on small data sets (3 existing entries of X/XDetail), but when I attempted the real file and went to churn through it's ~7700 entries I seem to have endless processing.
For a sample data set of the following already contained in X/XDetail:
XID: 1, Name: Bob, ID: 10
XID: 2, Name: Joe, ID: 20
XID: 3, Name: Sam, ID: 30
With a results file containing:
XID: 2, Name: Bob2
XID: 3, Name: NotSam
XID: 4, Name: NewGuy
XID: 5, Name: NewGuy2
I'd like to be able to get a result set containing:
{XID: 2, Name: Bob2}, x, xDetail
{XID: 3, Name: NotSam}, x, xDetail
{XID: 4, Name: NewGuy}, x, xDetail
{XID: 5, Name: NewGuy2}, x, xDetail
I'd like the x and xDetail as part of the result set so that I can use those typed data rows to make the necessary changes.
I tried my hand at making such a query:
var newData = from result in results
join x in XList on result.XID equals x.XID
join xDetail in XDetailList on x.ID equals xDetail.ID
where
(x.XID == result.XID && xDetail.Name != result.Name)
select new
{
XValue = x,
XDetailValue = xDetail,
Result = result
};
As the joins indicate I'm only ever going to get the changed items in the data, I really want to be able to add in that data that isn't in X/XDetail and stop my system that has been processing my ~7700 change file for the past 2.5 hours. I feel like I have stared at this and related queries too long to be able to spot what I should be doing to shape a where clause correctly for it.
Is there a way to structure the linq query to find the changed data and the data that does not exist in X/XDetail and return that into a new result set to process?
I think your performaces problems are related to the complexity of your queries, that are maybe around O(n^2).
Hence, first I suggest you to set the current data in a lookup structure, like this (*):
var joinedByXID = (from x in XList
join xDetail in XDetailList on x.ID equals xDetail.ID
select new { X = x, XDetail = xDetail })
.ToLookup(x => x.X.ID);
Now, I'm not sure, but I assume that by saying "changed data" you mean a list of entries having XID already existing but a new name, is it right?
If so, you can get "changed data" using this query:
var changedData = results
.Where(r => joinedByXID.Contains(r.XID))
.SelectMany(r => joinedByXID[r.XID]
.Where(x => x.XDetail.Name != r.Name)
.Select(old => new {XValue=old.X, XDetailValue=old.XDetail, Result=r}));
Then, if by "new data" you mean a list of entries having new XID (XID not currently present in XList/XDetailList), well you cannot match them with X/Xdetail elements because, well there aren't any, so that's simply:
var newData = results
.Where(r => !joinedByXID.Contains(r.XID));
(*)
Actually, to be even faster, you could arrange your data in a dictionary of dictionary, where the outer key is XID and the inner key is the Name.

LINQ Join with Multiple From Clauses

When writing LINQ queries in C#, I know I can perform a join using the join keyword. But what does the following do?
from c in Companies
from e in c.Employees
select e;
A LINQ book I have say it's a type of join, but not a proper join (which uses the join keyword). So exactly what type of join is it then?
Multiple "from" statements are considered compound linq statments. They are like nested foreach statements. The msdn page does list a great example here
var scoreQuery = from student in students
from score in student.Scores
where score > 90
select new { Last = student.LastName, score };
this statement could be rewritten as:
SomeDupCollection<string, decimal> nameScore = new SomeDupCollection<string, float>();
foreach(Student curStudent in students)
{
foreach(Score curScore in curStudent.scores)
{
if (curScore > 90)
{
nameScore.Add(curStudent.LastName, curScore);
}
}
}
This will get translated into a SelectMany() call. It is essentially a cross-join.
Jon Skeet talks about it on his blog, as part of the Edulinq series. (Scroll down to Secondary "from" clauses.)
The code that you listed:
from c in company
from e in c.Employees
select e;
... will produce a list of every employee for every company in the company variable. If an employee works for two companies, they will be included in the list twice.
The only "join" that might occur here is when you say c.Employees. In an SQL-backed provider, this would translate to an inner join from the Company table to the Employee table.
However, the double-from construct is often used to perform "joins" manually, like so:
from c in companies
from e in employees
where c.CompanyId == e.CompanyId
select e;
This would have a similar effect as the code you posted, with potential subtle differences depending on what the employees variable contains. This would also be equivalent to the following join:
from c in companies
join e in employees
on c.CompanyId equals e.CompanyId
select e;
If you wanted a Cartesian product, however, you could just remove the where clause. (To make it worth anything, you'd probably want to change the select slightly, too, though.)
from c in companies
from e in employees
select new {c, e};
This last query would give you every possible combination of company and employee.
All the first set of objects will be joined with all the second set of objects. For example, the following test will pass...
[TestMethod()]
public void TestJoin()
{
var list1 = new List<Object1>();
var list2 = new List<Object2>();
list1.Add(new Object1 { Prop1 = 1, Prop2 = "2" });
list1.Add(new Object1 { Prop1 = 4, Prop2 = "2av" });
list1.Add(new Object1 { Prop1 = 5, Prop2 = "2gks" });
list2.Add(new Object2 { Prop1 = 3, Prop2 = "wq" });
list2.Add(new Object2 { Prop1 = 9, Prop2 = "sdf" });
var list = (from l1 in list1
from l2 in list2
select l1).ToList();
Assert.AreEqual(6, list.Count);
}

Categories

Resources