LINQ LEFT JOIN, Combine multiple WHERE clauses with OR - c#

LINQ,
var clause = PredicateBuilder.False<User>();
clause = clause.Or(u => u.uid.Equals(1));
clause = clause.Or(u => u.uid.Equals(2));
var usersInGroup = (from u in db.Users
join g in db.GroupUsers
on u.uid equals g.uid
into ug
from g in ug.DefaultIfEmpty()
where g.gid.Equals(0)
select u).Where(clause);
These two where clauses are chained together as;
WHERE ([t0].[gid] = 0) AND (([t1].[uid] = 1) OR ([t1].[uid] = 2))
How do I add the two where conditions as
WHERE ([t0].[gid] = 0) OR (([t1].[uid] = 1) OR ([t1].[uid] = 2))

Thanks to,
Can PredicateBuilder generate predicates that span multiple tables?
I now have a solution that works but my result set is based on a new hybrid class. As a result I have had to mirror all of the relevant fields. See below.
public class HybridGroupUser {
private User _user;
public User User {
get { return _user; }
set {
_user = value;
if (value != null) {
uid = value.uid;
fname = value.fname;
lname = value.lname;
email = value.email;
}
}
}
private GroupUser _GroupUser;
public GroupUser GroupUser {
get { return _GroupUser; }
set {
_GroupUser = value;
if (value != null) {
uid = value.uid;
fname = value.fname;
lname = value.lname;
email = value.email;
}
}
}
public int? uid { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public string email { get; set; }
}
With this class I can now do the following;
var clause = PredicateBuilder.False<HybridGroupUser>();
clause = clause.Or(u => u.GroupUser.gid.Equals(0);
foreach (int i in AddedUsers) {
int tmp = i;
clause = clause.Or(u => u.User.uid.Equals(tmp));
}
var usersInGroup = (from u in db.Users
join gusr in db.GroupUser
on u.uid equals gusr.uid
into ug
from gusr in ug.DefaultIfEmpty()
select new HybridGroupUser {
User = u,
GroupUser = gusr
}).Where(clause);

var usersInGroup = (from u in db.Users
join g in db.Groups
on u.uid equals g.uid
where g.gid.Equals(0) || (u.uid.Equals(1) || u.uid.Equals(2))
select u)

Instead of doing multiple Or clauses, why not just do Contains. That way your list of Ids can be complete dynamic (as long as it doesn't exceed 2000):
var ids = new int[] {1, 2, 3};
var usersInGroup = (from u in db.Users
join g in db.GroupUsers
on u.uid equals g.uid
into ug
from g in ug.DefaultIfEmpty()
where g.gid.Equals(0)
&& ids.Contains(u.uid)
select u);

Related

How to Get Last Record Using LINQ - Entity Framework in SQLite

I need to translate the following SQLite query to LINQ in C#
SELECT sup.SupplierName, sup.SupplierID, pr.Price, max(pr.AddedDate)
FROM Suppliers sup
LEFT JOIN ItemsPrices pr
USING(SupplierID)
WHERE pr.ItemID = '22'
GROUP BY sup.SupplierName
I've searched about all web site and tried the following LINQ query and it does group like what I want but doesn't select latest date. I'm newbie in LINQ please help me
internal List<LatestPriceDbo> GetLatestPrice(int _itemid)
{
using (dbContext context = new dbContext())
{
var groupedPrice = from a in context.ItemsPrices
where a.ItemId == _itemid
orderby a.Id descending
group a by new { a.ItemId, a.SupplierId } into g
select new ItemsPrice
{
SupplierId = g.Key.SupplierId,
ItemId = g.Key.ItemId,
Price = g.FirstOrDefault().Price,
AddedDate = g.Max(s => s.AddedDate)
};
var result = (from c in context.Suppliers
from k in groupedPrice
where k.ItemId == _itemid && c.SupplierId == k.SupplierId
select new LatestPriceDbo
{
supid = c.SupplierId,
supname = c.SupplierName,
price = k.Price,
addeddate = k.AddedDate
}).ToList();
return result;
}
}
internal class LatestPriceDbo
{
public int supid { get; set; }
public string supname { get; set; }
public decimal price { get; set; }
public string addeddate { get; set; }
}
I am using Database-First.
You should be able to use a LINQ Join I have mocked up something which might point you in the correct direction:
Notes
use the join first to get the set you looking for
you can then do a nested select for the max based on supplierId.
from a in context.ItemsPrices
join s in context.Suppliers on a.SupplierId equals s.supplierId
where a.ItemId == _itemid
orderby a.Id descending
select new ItemsPrice
{
SupplierName = s.SupplierName
SupplierId = a.SupplierId,
ItemId = a.ItemId,
Price = a.FirstOrDefault().Price,
AddedDate = context.ItemsPrices.Where(x => x.SupplierId == a.SupplierId).Max(s => s.AddedDate)
};
I solved the problem owing to Kevin's suggestion. I did need to be more search on web to improve the code block of Kevin's comment and I did.
internal List<LatestPriceDbo> GetLatestPrice(int _itemid)
{
using (dbContext context = new dbContext())
{
var result = (from a in context.ItemsPrices
join s in context.Suppliers on a.SupplierId equals s.SupplierId
where a.ItemId == _itemid
orderby a.Id descending
group new { a, s } by new { a.SupplierId, a.ItemId } into grb
select new LatestPriceDbo
{
supname = grb.FirstOrDefault().s.SupplierName,
supid = grb.Key.SupplierId,
itemid = grb.Key.ItemId,
price = context.ItemsPrices
.Where(x => x.ItemId == grb.FirstOrDefault().a.ItemId)
.OrderByDescending(z => z.Id).Select(z => z.Price)
.FirstOrDefault(),
addeddate = context.ItemsPrices
.Where(x => x.SupplierId == grb.FirstOrDefault().a.SupplierId)
.Max(s => s.AddedDate)
}).ToList();
return result;
}
}
internal class LatestPriceDbo
{
public int itemid { get; set; }
public int supid { get; set; }
public string supname { get; set; }
public decimal price { get; set; }
public string addeddate { get; set; }
public int recordid { get; set; }
}

How to bind linq data into Model class

I have following query,
var userTree = (from user in users
join location in locations on user.FkHomeLocationId equals location.LocationId
join region in regions on location.RegionId equals region.LocationId
group region by new {
Regionid = region.LocationId,
location.LocationId
}
into grp
select new {
RegionName = regions.FirstOrDefault(s = >s.LocationId == grp.Key.Regionid).LocationName,
Branches = new {
BranchName = locations.FirstOrDefault(b = >b.RegionId == grp.Key.Regionid).LocationName,
Users = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).Select(s = >new {
FullName = s.FirstName + " " + s.LastName
}).ToList()
}
}).ToList();
I want to bind the query result into model. how can I do this. I created following class model to achieve that,
public class TreeModel
{
public string RegionName { get; set; }
public Branch Branchs { get; set; }
}
public class Branch
{
public string BranchName { get; set; }
public List<User> Users { get; set; }
}
public class User
{
public string FirstName { get; set; }
}
Now need to bind above query data into this model class? Have any best way to do this without creating Branch and User classes? because my application have another classes with the same name of User and Branch and I feel some guilty to create separate classes like this for query data binding? I'm I correct?
Updated: I tried as follows,
var userTree = (from user in users
join location in locations on user.FkHomeLocationId equals location.LocationId
join region in regions on location.RegionId equals region.LocationId
group region by new {
Regionid = region.LocationId,
location.LocationId
}
into grp
select new Application.Alerts.Models.AlertUserTreeViewModel() {
RegionName = regions.FirstOrDefault(s = >s.LocationId == grp.Key.Regionid).LocationName,
Branchs = new Application.Alerts.Models.Branch() {
BranchName = locations.FirstOrDefault(b = >b.RegionId == grp.Key.Regionid).LocationName,
Users = new Application.Alerts.Models.User {
FirstName = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).FirstOrDefault().FirstName
}
}
}).ToList();
But I'm getting compile error in this line,
Users = new Application.Alerts.Models.User {
FirstName = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).FirstOrDefault().FirstName
}
as
Cannot implicitly convert type 'Alerts.Models.User' to 'System.Collections.Generic.List<Alerts.Models.User>'
just add extra FirstName property to your anonymous classes
var userTree =
....
select new {
RegionName = ...
Branches = new {
BranchName = ...,
Users = ....Select(s = >new {
FirstName=s.FirstName,
FullName = s.FirstName + " " + s.LastName
}).ToList()
}
after this you can create view model
var treeModel= userTree.Select( new TreeModel {
RegionName = userTree.RegionName
Branchs =userTree.Branches.Select(i=>
new Branch {
BranchName= i.BranchName,
Users= i.Users.Select(j=> new User{ FirstName=j.Firstname})
)});
if you need only fullname in your userTree you can convert back
userTree= userTree.Select( new TreeModel {
RegionName = userTree.RegionName
Branchs =userTree.Branches.Select(i=>
new Branch {
BranchName= i.BranchName,
Users= i.Users.Select( j=> new {j.FullName})
)});
UPDATE
if you want to run another query using dbcontext you can try this
var userTree = (from user in users
join location in locations on user.FkHomeLocationId equals location.LocationId
join region in regions on location.RegionId equals region.LocationId
group region by new {
Regionid = region.LocationId,
location.LocationId
}
into grp
select new Application.Alerts.Models.AlertUserTreeViewModel {
RegionName = regions.FirstOrDefault(s = >s.LocationId == grp.Key.Regionid).LocationName,
Branches = new new Application.Alerts.Models.Branch {
BranchId = locations.FirstOrDefault(b = >b.RegionId ==
grp.Key.Regionid).LocationId,
BranchName = locations.FirstOrDefault(b = >b.RegionId == grp.Key.Regionid).LocationName,
Users = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).Select(s = >new User {
Id=s.Id,
LastName=s.LastName,
FirstName= s.FirstName
}).ToList()
}
}).ToList();
Your Branch has a List<User> but you are trying to instantiate as a User that's why you are getting the error. Use below code.
Users = users.Where(u = >u.FkHomeLocationId == grp.Key.LocationId).Select(u => new User { FirstName = u.FirstName }).ToList()

Linq query with Average

I have 3 tables:
tblCompany : Id, Name, Location
tblRating : Id, CompanyId, Rate
tblImages : Id, CompanyId, ImagePath
I have a class
public class Company
{
public string Id { get; set; }
public string CompanyName { get; set; }
public string Location { get; set; }
public string AverageRate { get; set; }
public List<string> ImagePath { get; set; }
}
I want a LINQ query to produce a result to match the Company class.
I wrote this query but it does not work
List<Company> result = null;
using (DataContext dc = new DataContext())
{
result = (from a in dc.GetTable<tblCompany>()
join b in dc.GetTable<tblRating>()
on a.Id equals b.CompanyId
join c in dc.GetTable<tblImages>()
on a.Id equals c.CompanyId
select new SearchResult
{
CompanyName = a.Company,
Location = a.Location,
AverageRate = b.Rate.Average(),
ImagePath = c.ImagePath.ToList()
}).ToList<Company>();
}
Edited for whole Query:
(I've to say i'm sorry but i have no way of testing this query yet)
You can use the let clause instead of the joins:
var result = (from c in dc.GetTable<tblCompany>()
let r = (from re in dc.GetTable<tblRating>()
where re.CompanyId == c.Id && re.Rate != null
select re.Rate)
let i = (from im in dc.GetTable<tblImages>()
where im.CompanyId == c.Id
select im.ImagePath)
select new SearchResult
{
CompanyName = c.Name,
Location = c.Location,
AverageRate = r.Average(),
ImagePath = i.ToList()
}).ToList<Company>();
try this -
result = (from a in dc.GetTable<tblCompany>()
join b in dc.GetTable<tblRating>()
on a.Id equals b.CompanyId
join c in dc.GetTable<tblImages>()
on a.Id equals c.CompanyId
group new { b.Rate, c.ImagePath}
by new { a.Id, a.Location,a.Name} into groupList
select new Company
{
CompanyName = groupList.Key.Name,
Location = groupList.Key.Location,
AverageRate = groupList.Average(a=>a.Rate),
ImagePath = groupList.Select(i=>i.ImagePath).ToList()
}).ToList<Company>();

Conditional Joins With Linq

Is there a way to progressively / conditionally add joins to a query? I am creating a custom reporting tool for a client, and the client is given a list of objects he/she can select to query on. There will always be a base object used in the query ("FWOBid").
So, for example, if the customer selects objects "FWOBid", "FWOItem", and "FWOSellingOption", I'd want to do this:
var query = from fb in fwoBids
// if "FWOSellingOption", add this join
join so in sellingOptions on fb.Id equals so.BidId
// if "FWOItem", add this join
join i in fwoItems on fb.Id equals i.FWOBidSection.BidId
// select "FWOBid", "FWOItem", and "FWOSellingOption" (everything user has selected)
select new { FWOBid = fb, FWOSellingOption = so, FWOItem = i };
The trick is the customer can select about 6 objects that are all related to each other, resulting in many different combinations of joins. I'd like to avoid hard coding those if possible.
One option is to do some custom join combined with left joins.
A decent TSQL backend should not get any drawbacks in terms of performance for always using all the joins, since the optimers would just remove the join if the condition is always false. But this should be checked out.
bool joinA = true;
bool joinB = false;
bool joinC = true;
var query = from fb in fwoBids
join so in sellingOptions on new { fb.Id, Select = true } equals new { Id = so.BidId, Select = joinA } into js
from so in js.DefaultIfEmpty()
join i in fwoItems on new { fb.Id, Select = true } equals new { Id = i.FWOBidSection.BidId, Select = joinB } into ji
from i in ji.DefaultIfEmpty()
join c in itemsC on new { fb.Id, Select = true } equals new { Id = c.BidId, Select = joinC }
select new
{
FWOBid = fb,
FWOSellingOption = so,
FWOItem = i,
ItemC = c
};
In the Linq query syntax this is not possible, or looking at the other answers hardly readable. Not much more readable but another possibility would be to use the extension methods (sort of pseudo code):
bool condition1;
bool condition2;
List<Bid> bids = new List<Bid>();
List<SellingOption> sellingOptions = new List<SellingOption>();
List<Item> items = new List<Item>();
var result = bids.Select(x => new {bid = x, sellingOption = (SellingOption)null, item = (Item)null});
if (condition1)
result = result.Join(
sellingOptions,
x => x.bid.Id,
x => x.BidId,
(x, sellingOption) => new { x.bid, sellingOption, item = (Item)null });
if (condition2)
result = result.Join(
items,
x => x.bid.Id,
x => x.BidId,
(x, item) => new { x.bid, x.sellingOption, item });
Just see this as a sort of a concept. It is essentially the same that Peter Duniho did.
The thing is, if you don't want to immediately join on all options if not necessary, then it won't look that nice. Perhaps you should try to join all now and don't worry about performance. Have you ever measured how slow or fast it might be? Think of it as "I don't need it now!". If performance is indeed a problem, then you can act on it. But if it is not, and you won't know if you never tried, then leave it as the six joins you mentioned.
It's hard to provide a really good example solution without a really good example problem. However, what I mean by "chain the queries" is something like this:
var query = from x in dba select new { A = x, B = (B)null, C = (C)null };
if ((joinType & JoinType.B) != 0)
{
query = from x in query
join y in dbb on x.A.Id equals y.Id
select new { A = x.A, B = y, C = x.C };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.A.Id equals y.Id
select new { A = x.A, B = x.B, C = y };
}
That is, based on the appropriate condition, query the previous result with another join. Note that to do this successfully, each query must produce the same type. Otherwise, it's not possible to assign a new query to the previous query result variable.
Note that while in the above, I simply have a separate property for each possible input type, I could have instead had the type simply have properties for the input columns, Id, Name, and then the Text properties from the B and C types (which would have to be named differently in the query result type, e.g. TextB and TextC). That would look like this:
var query = from x in dba select new { Id = x.Id, Name = x.Name,
TextB = (string)null, TextC = (string)null };
if ((joinType & JoinType.B) != 0)
{
query = from x in query
join y in dbb on x.Id equals y.Id
select new { Id = x.Id, Name = x.Name, TextB = y.Text, TextC = x.TextC };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.Id equals y.Id
select new { Id = x.Id, Name = x.Name, TextB = x.TextB, TextC = y.Text };
}
Here is a complete code example that includes the above logic in a runnable program:
class A
{
public string Name { get; private set; }
public int Id { get; private set; }
public A(string name, int id)
{
Name = name;
Id = id;
}
public override string ToString()
{
return "{" + Name + ", " + Id + "}";
}
}
class B
{
public int Id { get; private set; }
public string Text { get; private set; }
public B(int id, string text)
{
Id = id;
Text = text;
}
public override string ToString()
{
return "{" + Id + ", " + Text + "}";
}
}
class C
{
public int Id { get; private set; }
public string Text { get; private set; }
public C(int id, string text)
{
Id = id;
Text = text;
}
public override string ToString()
{
return "{" + Id + ", " + Text + "}";
}
}
[Flags]
enum JoinType
{
None = 0,
B = 1,
C = 2,
BC = 3
}
class Program
{
static void Main(string[] args)
{
A[] dba =
{
new A("A1", 1),
new A("A2", 2),
new A("A3", 3)
};
B[] dbb =
{
new B(1, "B1"),
new B(2, "B2"),
new B(3, "B3")
};
C[] dbc =
{
new C(1, "C1"),
new C(2, "C2"),
new C(3, "C3")
};
JoinType joinType;
while ((joinType = _PromptJoinType()) != JoinType.None)
{
var query = from x in dba select new { A = x, B = (B)null, C = (C)null };
if ((joinType & JoinType.B) != 0)
{
query = from x in query
join y in dbb on x.A.Id equals y.Id
select new { A = x.A, B = y, C = x.C };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.A.Id equals y.Id
select new { A = x.A, B = x.B, C = y };
}
foreach (var item in query)
{
Console.WriteLine(item);
}
Console.WriteLine();
}
}
private static JoinType _PromptJoinType()
{
JoinType? joinType = null;
do
{
Console.Write("Join type ['A' for all, 'B', 'C', or 'N' for none]");
ConsoleKeyInfo key = Console.ReadKey();
Console.WriteLine();
switch (key.Key)
{
case ConsoleKey.A:
joinType = JoinType.BC;
break;
case ConsoleKey.B:
joinType = JoinType.B;
break;
case ConsoleKey.C:
joinType = JoinType.C;
break;
case ConsoleKey.N:
joinType = JoinType.None;
break;
default:
break;
}
} while (joinType == null);
return joinType.Value;
}
}
I hope this is an improvement over previous answers.
public class Bids
{
public int Id { get; set; }
public double Price { get; set; }
}
public class BidSection
{
public int BidId { get; set; }
}
public class SellingOptions
{
public int BidId { get; set; }
public int Quantity { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public BidSection FWOBidSection { get; set; }
}
public class ConditionalJoin
{
public bool jOpt1 { get; set; }
public bool jOpt2 { get; set; }
public ConditionalJoin(bool _joinOption1, bool _joinOption2)
{
jOpt1 = _joinOption1;
jOpt2 = _joinOption2;
}
public class FBandSo
{
public Bids FWOBids { get; set; }
public SellingOptions FWOSellingOptions { get; set; }
}
public class FBandI
{
public Bids FWOBids { get; set; }
public Item FWOItem { get; set; }
}
public void Run()
{
var fwoBids = new List<Bids>();
var sellingOptions = new List<SellingOptions>();
var fwoItems = new List<Item>();
fwoBids.Add(new Bids() { Id = 1, Price = 1.5 });
sellingOptions.Add(new SellingOptions() { BidId = 1, Quantity = 2 });
fwoItems.Add(new Item() { ItemId = 10, FWOBidSection = new BidSection() { BidId = 1 } });
IQueryable<Bids> fb = fwoBids.AsQueryable();
IQueryable<SellingOptions> so = sellingOptions.AsQueryable();
IQueryable<Item> i = fwoItems.AsQueryable();
IQueryable<FBandSo> FBandSo = null;
IQueryable<FBandI> FBandI = null;
if (jOpt1)
{
FBandSo = from f in fb
join s in so on f.Id equals s.BidId
select new FBandSo()
{
FWOBids = f,
FWOSellingOptions = s
};
}
if (jOpt2)
{
FBandI = from f in fb
join y in i on f.Id equals y.FWOBidSection.BidId
select new FBandI()
{
FWOBids = f,
FWOItem = y
};
}
if (jOpt1 && jOpt2)
{
var query = from j1 in FBandSo
join j2 in FBandI
on j1.FWOBids.Id equals j2.FWOItem.FWOBidSection.BidId
select new
{
FWOBids = j1.FWOBids,
FWOSellingOptions = j1.FWOSellingOptions,
FWOItems = j2.FWOItem
};
}
}
}

LINQ to Entities does not recognize the method, 2 repositories

I keep getting the error below on my code, and can't understand why it is having problems translating it to a query, it is pretty simple.
I have 2 repositories, Album and AlbumImage, when I fetch an album do I want a cover, that is a subselect in AlbumImages. What am I doing wrong here?
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[Sogaard.us.Cosplay.Data.AlbumImage] Get()'
method, and this method cannot be translated into a store expression.
Album repository
public class AlbumRepository : IRepository<Album>
{
private CosplayEntities _entities;
private IRepository<AlbumImage> _imageRepository;
public AlbumRepository(CosplayEntities entities, IRepository<AlbumImage> imageRepository)
{
_entities = entities;
_imageRepository = imageRepository;
}
public IQueryable<Album> Get()
{
return (from a in _entities.Albums
select new Album()
{
Id = a.Id,
UserId = a.UserId,
Name = a.Name,
Created = a.Created,
LastEdit = a.LastEdit,
Description = a.Description,
Views = a.Views,
Location = a.Location,
Photoshoot = a.Photoshoot,
Cover = (from ai in _imageRepository.Get() where ai.AlbumId == a.Id orderby ai.Cover descending, ai.Id ascending select ai).FirstOrDefault(),
});
}
}
AlbumImage repository
public class AlbumImageRepository : IRepository<AlbumImage>
{
private CosplayEntities _entities;
public AlbumImageRepository(CosplayEntities entities)
{
_entities = entities;
}
public IQueryable<AlbumImage> Get()
{
return (from ai in _entities.AlbumImages
select new AlbumImage()
{
Id = ai.Id,
AlbumId = ai.AlbumId,
UserId = ai.UserId,
Type = ai.Type,
Width = ai.Width,
Height = ai.Height,
Description = ai.Description,
Views = ai.Views,
Uploadet = ai.Uploadet,
LastView = ai.LastView,
Thumblink = ai.Thumblink,
Imagelink = ai.Imagelink,
Cover = ai.Cover
});
}
This is the code i am getting the error on
_albumImageRepository = new AlbumImageRepository(_entities);
_albumRepository = new AlbumRepository(_entities, _albumImageRepository);
_albumImagesTagRepository = new AlbumImagesTagRepository(_entities);
....
var album = _albumRepository.Get().Where(x => x.Id == image.AlbumId).FirstOrDefault();
Update: I have commented the Cover = ... out in my IQueryable Get() so it is 2 simple select as object.
And i still get the error in something as simple as
model.Albums = (from a in _albumRepository.Get()
orderby a.Id descending
select new AlbumDisplayModel()
{
Album = a,
ImageCount = _albumImageRepository.Get().Where(x => x.AlbumId == a.Id).Count(),
User = _userRepository.Get().Where(x => x.Id == a.UserId).FirstOrDefault()
})
.Skip(AlbumsPrPage * (page - 1))
.Take(AlbumsPrPage).ToList();
Update 2: If i rewrite the IQueryable Get() to the following, do it work flawlessly, there there should really be no diffrence in how it is handled?
public IQueryable<Album> Get()
{
return (from a in _entities.Albums
select new Album()
{
Id = a.Id,
UserId = a.UserId,
Name = a.Name,
Created = a.Created,
LastEdit = a.LastEdit,
Description = a.Description,
Views = a.Views,
Location = a.Location,
Photoshoot = a.Photoshoot,
Cover = (from ai in _entities.AlbumImages where ai.AlbumId == a.Id orderby ai.Cover descending, ai.Id ascending select new AlbumImage()
{
Id = ai.Id,
AlbumId = ai.AlbumId,
UserId = ai.UserId,
Type = ai.Type,
Width = ai.Width,
Height = ai.Height,
Description = ai.Description,
Views = ai.Views,
Uploadet = ai.Uploadet,
LastView = ai.LastView,
Thumblink = ai.Thumblink,
Imagelink = ai.Imagelink,
Cover = ai.Cover
}).FirstOrDefault(),
});
}
Update 3: Did a little test, and the problem seems to be with Entity framework, se the following code, The var linqAlbum = testClass.LinqAlbumGet().ToList(); executes without any problems and return the correct data, var eeAlbum = testClass.EEAlbumGet().ToList(); fails with the exception
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[RepositoryTest.TestAlbumCover] EEImageGet()'
method, and this method cannot be translated into a store expression.
My test script
class Program
{
static void Main(string[] args)
{
var linq = new LinqDataContext();
var ee = new NewCosplayEntities();
var testClass = new Test(linq, ee);
var linqAlbum = testClass.LinqAlbumGet().ToList();
var eeAlbum = testClass.EEAlbumGet().ToList();
}
}
public class Test
{
public NewCosplayEntities ee { get; set; }
public LinqDataContext linq { get; set; }
public Test(LinqDataContext linq, NewCosplayEntities ee)
{
this.linq = linq;
this.ee = ee;
}
public IQueryable<TestAlbum> LinqAlbumGet()
{
return from a in linq.Albums
select new TestAlbum
{
Id = a.Id,
Name = a.Name,
Cover = (from i in LinqImageGet() where i.AlbumId == a.Id select i).FirstOrDefault()
};
}
public IQueryable<TestAlbumCover> LinqImageGet()
{
return from i in linq.AlbumImages
select new TestAlbumCover()
{
Id = i.Id,
AlbumId = i.AlbumId
};
}
public IQueryable<TestAlbum> EEAlbumGet()
{
return from a in ee.Albums
select new TestAlbum
{
Id = a.Id,
Name = a.Name,
Cover = (from i in EEImageGet() where i.AlbumId == a.Id select i).FirstOrDefault()
};
}
public IQueryable<TestAlbumCover> EEImageGet()
{
return from i in ee.AlbumImages
select new TestAlbumCover()
{
Id = i.Id,
AlbumId = i.AlbumId
};
}
}
public class TestAlbum
{
public int Id { get; set; }
public string Name { get; set; }
public TestAlbumCover Cover { get; set; }
}
public class TestAlbumCover
{
public int Id { get; set; }
public int AlbumId { get; set; }
}
Your problem comes in the ItemRepository for Albumn. Specifically because _entities has no knowledge of the _imageRepository type, so it doesn't know how to translate that type into the appropriate TSQL script. You could cast the _entities.Albums.ToList() to force the IQueryable into an IEnumerable before you try to access the _ImageRepository.Get() from the scope of the hydrated object instead of directly on the database instance. Realize that you are then going to see a perf hit on the n+1 database requests for the AlbumImage child objects for each Album.
public IQueryable<Album> Get()
{
return (from a in _entities.Albums
select new Album()
{
Id = a.Id,
UserId = a.UserId,
Name = a.Name,
Created = a.Created,
LastEdit = a.LastEdit,
Description = a.Description,
Views = a.Views,
Location = a.Location,
Photoshoot = a.Photoshoot,
Cover = (from ai in _imageRepository.Get() where ai.AlbumId == a.Id orderby ai.Cover descending, ai.Id ascending select ai).FirstOrDefault(),
});
}
Ultimately, the problem is that your trying to use an ActiveRecord pattern rather than a true repository. Everything in a single IQueryable needs to be fetched through the same database context instance for parsing and tracking purposes.
Potentially its because you are wrapping the Album and AlbumImage in new references. I would remove that and do the projection after your query.
I don't think you can project into an entity and have each projection use a result from another IQueryable. If you replaced the contents of IQueryable<AlbumImage> Get() with this, it might work:
from a in _entities.Albums
join c in _imageRepository.Get() on a.Id equals c.AlbumId into acJoin
from ac in acJoin.DefaultIfEmpty()
select new Album()
{
Id = a.Id,
etc..,
etc..,
Cover = ac
}
I'm actually fairly certain that you will need to adjust this freehand query, but essentially it's joining the IQueryables, then projecting those results into your objects, instead of projecting to your objects then inserting an IQueryable into those results. Not the best explanation I know, but just look up "LINQ Left Join" or "Linq Left Outer Join" to see the syntax of what I'm describing here. Example

Categories

Resources