Let's assume I have multiple db tables, I'll just represent them as lists for convenience:
EntitySource {
public int Id {get; set;}
public ICollection<Entity_1> table_1 { get; set }
public ICollection<Entity_2> table_2 { get; set }
public ICollection<Entity_3> table_3 { get; set }
}
Entity_1/Entity_2/Entity_3 {
public int Id { get; set; }
public string Name { get; set; }
}
List<Entity_1> table1 = new List<Entity_1>() {new Entity_1{Id = 1, Name = "First"}, new Entity_1{Id = 2, Name = "Second"}
List<Entity_2> table2 = new List<Entity_2>() {new Entity_2{Id = 3, Name = "First"}, new Entity_2{Id = 4, Name = "Second"}
List<Entity_3> table3 = new List<Entity_3>() {new Entity_3{Id = 5, Name = "First"}, new Entity_3{Id = 6, Name = "Second"}
I'm querying against EntitySource which contains references to multiple collections that I want to query against and map to MergedList class that contains two properties, Id of the entity source and one collection containing all merged collections of the EntitySource.
What I want to achieve is query for only id's and map them to single list of integers.
Something like this:
var entities = await entitySource.Queryable()
.Select(e => new MergedList()
{
PrincipalId = e.Id,
CombinedIds = e.table1.Select(e => e.Id)
.Concat(e.table2.Select(e => e.Id)
.Concat(e.table3.Select(e => e.Id)
})
.ToListAsync(cancellationToken);
public class MergedList {
public int PrincipalId {get;set;}
public IEnumerable<int> CombinedIds {get;set;}
}
But apparently the above statement is not working, expression could not be parsed.
Unable to translate a collection subquery in a projection since either
parent or the subquery doesn't project necessary information required
to uniquely identify it and correctly generate results on the client
side.
I'm using Entity Framework Core v6.0
You can combine them on the client side.
var filtered = entitySource.Queryable()
.Where(ent => input.Id == ent.Id);
var rawData = await
filtered.SelectMany(e => e.table1.Select(t => new { e.Id, SubId = t.Id } ))
.Concat(filtered.SelectMany(e => e.table2.Select(t => new { e.Id, SubId = t.Id } ))
.Concat(filtered.SelectMany(e => e.table3.Select(t => new { e.Id, SubId = t.Id } ))
.ToListAsync(cancellationToken);
var entities = rawData.GroupBy(x => x.Id)
.Select(g => new MergedList()
{
PrincipalId = g.Key,
CombinedIds = g.Select(x => x.SubId).ToList()
})
.ToList();
Related
I've spent the afternoon on StackOverflow and Google looking for a way to do a simple SQL query using linq to entities. I am trying to get data from two tables joined by a many-to-many relationship. In SQL I would write the query like this:
SELECT v.[VendorID]
, t.[UnitNumber]
, t.[Name]
, t.[Address]
, t.[CityStateZip]
FROM [Tenant] t
INNER JOIN [TenantVendor] tv ON tv.[TenantID] = t.[TenantID]
INNER JOIN [Vendor] v on v.[VendorID] = tv.[VendorID]
WHERE t.[UnitNumber] LIKE '%100A%'
AND t.[CompanyID] = 17874;
I have an object in a list that I'm selecting the data into that looks like this which gets applied directly to a grid:
public class SearchObject
{
public int IDField { get; set; }
public string UniqueField { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string CityStateZip { get; set; }
}
Here's the kicker, the m2m table is only made up of the primary keys from both tables, so it doesn't have an entity to select. They show up under each others entity as a collection like this (from the tenants entity):
public virtual ICollection<Vendor> Vendors { get; set; }
I would prefer lambda expressions, but query would work fine too. The closest I have got are these two:
SearchData = DBContext.Tenants
.Where(t => t.Company.Name == CompanyName && t.UnitNumber.ToString().Contains(SearchText))
.OrderBy(DynamicSort)
.Skip(StartRow)
.Take(PageSize)
.Select(t => new SearchObject { IDField = t.Vendors, UniqueField = t.UnitNumber.ToString(), Name = t.Name, Address = t.Address, CityStateZip = t.CityStateZip })
.ToList();
That one doesn't work because t.Vendors is a collection, what I want is just the vendorID.
This one works but returns way too many records since it's missing the join between the two tables:
SearchData = (from t in DBContext.Tenants
from v in DBContext.Vendors
where t.Company.Name == CompanyName && t.UnitNumber.ToString().Contains(SearchText)
select new SearchObject { IDField = v.VendorID , UniqueField = t.UnitNumber.ToString(), Name = t.Name, Address = t.Address, CityStateZip = t.CityStateZip })
.ToList();
EDIT / UPDATE
After ElMent provieded me with the correct answer, I figured out how to achieve the same result using C# methods, SelectMany was the key.
SearchData = DBContext.Tenants
.Where(t => t.Company.Name == CompanyName && t.UnitNumber.ToString().Contains(SearchText))
.OrderBy(DynamicSort)
.Skip(StartRow)
.Take(PageSize)
.SelectMany(t=>t.Vendors.Select(v => new SearchObject { IDField = v.VendorID, UniqueField = t.UnitNumber.ToString() + " - " + v.VendorNumber, Name = t.Name, Address = t.Address, CityStateZip = t.CityStateZip }))
.ToList();
What about this. See "from v in t.Vendors" on second line.
SearchData = (from t in DBContext.Tenants
from v in t.Vendors
where t.Company.Name == CompanyName && t.UnitNumber.ToString().Contains(SearchText)
select new SearchObject { IDField = v.VendorID , UniqueField = t.UnitNumber.ToString(), Name = t.Name, Address = t.Address, CityStateZip = t.CityStateZip })
.ToList();
Mor info here:
https://msdn.microsoft.com/en-us/library/bb386932(v=vs.110).aspx
How to write 'Where Any In' in LINQ to Entity?
Here is my model :
class Chair
{
public int Id { get; set; }
public int TableId { get; set; }
public Table Table { get; set; }
}
class Table
{
public int Id { get; set; }
public ICollection<Chair> Chairs { get; set; }
public ICollection<Category> Categories { get; set; }
public Table()
{
Chairs = new List<Chair>();
Categories = new List<Category>();
}
}
class Category
{
public int Id { get; set; }
public ICollection<Table> Tables { get; set; }
}
I also got a simple list of Category :
List<Category> myCategories = new List<Category>(c,d,e);
I want to get only that Chairs that belongs to Table that got one of the Category from myCategories List. Thats what im trying to do :
var result =
ctx.Chairs.Where(x => x.Table.Categories.Any(y => myCategories.Any(z => z.Id == y.Id))).ToList();
I think its ok but what i get is error :
"Unable to create a constant value of type 'ConsoleApplication1.Category'. Only primitive types or enumeration types are supported in this context"
Try to compare with in-memory categories Ids collection, instead of categories collection.
var myCategoriesIds = myCategories.Select(c => c.Id).ToArray();
var result =
context.Chairs
.Where(
x => x.Table.Categories.Any(
y => myCategoriesIds.Contains(y.Id)))
.ToList();
this is because ctx.Chairs is a collection that is in database, you should retrieve that collection first in order to compare it with in-memory data:
var result = ctx
.Chairs
.AsEnumerable() // retrieve data
.Where(x =>
x.Table.Categories.Any(y =>
myCategories.Any(z => z.Id == y.Id)))
.ToList();
EDIT: that wouldn't be the correct thing to do if you have a lot of entities on database, what you can do is to split it into two queries:
var tables = ctx.Tables
.Where(x =>
x.Categories.Any(y =>
myCategories.Any(z => z.Id == y.Id)));
var result = ctx.Chairs
.Where(x =>
tables.Any(t=> t.Id == x.TableId))
.ToList();
You can select Ids from myCategories and use it last statement.
var CategoryIds = myCategories.Select(ct => ct.Id);
var result = ctx.Chairs.Where(x => x.Table.Categories.Any(y => CategoryIds.Any(z => z == y.Id))).ToList();
I have a requirement where I need to get a List of Model1 (List) using Linq, the Model1 have List of Model2 (List) in it and I need to fetch that also. For this I have created a Linq but m getting following error:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List1 [OurCourse]
ToList[OurCourse](System.Collections.Generic.IEnumerable1
[OurCourse])' method, and this method cannot be translated into a
store expression.
Please refer below for detail:
I have two tables Colleges and Courses, with following columns:
College: ID, Name, Contact, City, Address
Cource: ID, CollegeID, Name, Years
My project have two view models for them, as follows:
public class OurCollege
{
public string Name { get; set; }
public string Contact { get; set; }
public List<OurCourse> MyCourses { get; set; }
}
public class OurCourse
{
public string Name { get; set; }
public int NumberOfYears { get; set; }
}
Here the the query query which I have prepared but I am getting the error:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
How about doing like this:
MyCourses = from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}
Your complete query will look now:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = (from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
Actually LINQ to Entities converts your query into SQL.It doesn't know how to translate ToList() in SQL
An alternate is to change you List<T> to IEnumerable<T> and remove ToList() frim your original code:
public IEnumerable<OurCourse> MyCourses { get; set; }
and in query:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
})
}).ToList();
For more details visit this Entity Framework ToList() in nested type (LINQ to Entities does not recognize the method)
A foreach loop will work, you can write the query something like this
var colleges = db.Colleges
.Select(x => new OurCollege()
{
CollegeId = x.CollegeId,
Name = x.Name,
Contact = x.Contact
}).ToList();
foreach (var college in colleges)
{
college.MyCourse = db.Course.Where(x => x.CollegeId == college.CollegeId)
.Select(x => new OurCourse()
{
Name = x.Name,
NumberOfYears = x.Years
}).ToList()
}
Dictionary<int, string> D = new Dictionary<int, string>();
D.Add(0, "Insert");
D.Add(1, "Update");
D.Add(2, "Delete");
using (SerasMacEntity SME = new SerasMacEntity())
{
var SQL = (from p in SME.tbl_admin_islem
let testx = D.Where(x => x.Key == p.islem).Select(x => x.Value).FirstOrDefault()
orderby p.tarih descending
select new
{
p.id,
p.islem,
p.tarih
});
Store1.DataSource = SQL;
Store1.DataBind();
}
Its giving this error;
'System.Collections.Generic.KeyValuePair`2' and Only primitive types
('such as Int32, String, and Guid')
I use this method in Linq but I can't use Entity.
public class table
{
public int ID { get; set; }
public string Adi { get; set; }
public int IslemID { get; set; }
public table() { }
public table(int _ID, string _Adi, int _IslemID)
{
_ID = ID;
_Adi = Adi;
_IslemID = IslemID;
}
}
public List<table> table_list = new List<table>(new table[]
{
new table{ID=1,Adi="A1",IslemID=0},
new table{ID=2,Adi="A2",IslemID=1},
new table{ID=3,Adi="A3",IslemID=2},
new table{ID=4,Adi="A4",IslemID=1},
new table{ID=5,Adi="A5",IslemID=0},
new table{ID=6,Adi="A6",IslemID=2},
new table{ID=7,Adi="A7",IslemID=0},
new table{ID=8,Adi="A8",IslemID=1},
new table{ID=9,Adi="A9",IslemID=3}
});
public Dictionary<int, string> option_dictionary = new Dictionary<int,string>()
{
{0, "OK"},
{1, "NO"},
{2, "YA"},
{3, "OH"}
};
public void TestL()
{
string b = option_dictionary.Where(x => x.Key == 1).Select(x =>x.Value).First();
var SQL = (from p in table_list
let test = option_dictionary.Where(x => x.Key == p.IslemID).Select(x => x.Value).First()
select new
{
p.ID,
p.Adi,
test
}
);
}
This method is working in Linq. Its not working in Entity.
Thanks for your help.
LINQ is not the same as LINQ. Your working example uses a data structure in memory, namely List<table> table_list or more generally an IEnumerable<T>. Using the LINQ extension methods of IEnumerable<T> is called LINQ to Objects.
When you apply LINQ to an ObjectSet<T> or DbSet<T> of Entity Framework you are using the LINQ extension methods of IQueryable<T>. In this case you are using LINQ to Entities. They have the same name and you can write the same LINQ expressions like with LINQ to Objects and your code will compile fine.
But the big difference is that a LINQ to Entities query is not executed in memory (like LINQ to Objects) but it has to be translated into SQL to be executed in the database.
If your query is not translatable into SQL or the translation is not supported you get a runtime exception. In your specific case you have a complex in memory data structure - your dictionary - which cannot be used in a SQL query.
You have to rewrite your query to solve the problem. I would use the dictionary after you have executed the query, for example this way:
public class Helper
{
public int id { get; set; }
public int islem { get; set; }
public string textx { get; set; }
// ...
}
using (SerasMacEntity SME = new SerasMacEntity())
{
var SQL = (from p in SME.tbl_admin_islem
orderby p.tarih descending
select new Helper
{
id = p.id,
islem = p.islem,
//...
});
var list = SQL.ToList(); // excutes query in DB, rest happens in memory
foreach (var item in list)
{
item.testx = D.Where(x => x.Key == item.islem)
.Select(x => x.Value)
.FirstOrDefault();
}
Store1.DataSource = list;
Store1.DataBind();
}
I solve it this method;
var SQL = (from p in SME.tbl_admin_islem
orderby p.tarih descending
select new
{
p.id,
p.islem,
p.tarih
}).AsEnumerable().Select(s => new
{
s.id,
s.islem,
s.tarih,
testx = D.Where(x => x.Key == s.islem).Select(x => x.Value).FirstOrDefault()
});
or try this
var SQL = (from p in SME.tbl_admin_islem.AsEnumerable()
orderby p.tarih descending
select p).Select(s => new
{
s.id,
s.islem,
s.tarih,
testx = D.Where(x => x.Key == s.islem).Select(x => x.Value).FirstOrDefault()
});
I was convert my entity AsEnumerable() and then apply again Lambda query.
I have two classes:
class Customer
{
public string Name { get; set; }
public string ZipCode { get; set; }
public List<Order> OrderList { get; set; }
}
class Order
{
public string OrderNumber { get; set; }
}
Using LINQ, i want to get list of Orders group by ZipCode. If Zipcode "12121" has 10 customers and each has 2 orders then it should return me only one Zipcode with the list of 20 orders.
I am trying to do it like this but not able to figure out whats wrong
var orders = br.CustOrderList
.Select(r => new
{
r.ZipCode,
r.Name,
r.OrderList
})
.GroupBy(x => new { x.ZipCode, x.OrderList});
Any help please?
This should do what you want:
var orders = br.CustOrderList
.GroupBy(x => x.ZipCode)
.Select(g => new
{
ZipCode = g.Key,
Orders = g.SelectMany(x => x.OrderList)
});
var orders = br.CustOrderList
.Select(r => new
{
r.ZipCode,
r.Name,
r.OrderList
})
.GroupBy(x => x.ZipCode);
You just want to group by the ZipCode so just group by that
Ah yea just try
var order = br.CustOrderList
.GroupBy(x = x.ZipCode);
No need to select new items out of the list