LINQ to Entities does not recognize the method, 2 repositories - c#

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

Related

Using Join with IQueryable LINQ

I am trying to implement Inner join query on two tables opportunityProducts and Products where I am supposed to return Iqueryable element in my MVC web API service. But from below, I am not able to get result as it throws error for conversion.
public IQueryable<OpportunityProducts> GetProductsByShipID(int id)
{
IQueryable<OpportunityProducts> oppProductss =
from c in db.OpportunityProducts
from p in db.Products
where p.ProductID == c.ProductID
select new { c.Quantity,c.ProductDesc,c.RemainingQuantity, p.QtyInHand};
return oppProductss;
}
You need to fill the Type you wish to return instead of returning an anonymous type. Here since you are querying the OpportunityProducts, I think you don't have QtyInHand property. So you can either return a new type altogether or add this property.:-
IQueryable<ResultantProducts> oppProductss =
from c in db.OpportunityProducts
from p in db.Products
where p.ProductID == c.ProductID
select new ResultantProducts
{
Quantity = c.Quantity,
ProductDesc = c.ProductDesc,
RemainingQuantity = c.RemainingQuantity,
QtyInHand = p.QtyInHand
};
I see an error in your code. You should return objects of type OpportunityProducts, I mean:
public IQueryable<OpportunityProducts> GetProductsByShipID(int id)
{
IQueryable<OpportunityProducts> oppProductss = from c in db.OpportunityProducts
from p in db.Products
where p.ProductID == c.ProductID
select new OpportunityProducts // <---- THIS!
{
Quantity = c.Quantity,
ProductDesc = c.ProductDesc,
RemainingQuantity = c.RemainingQuantity,
QtyInHand = p.QtyInHand
};
return oppProductss;
}
I hope it helps you.
Regards,
Julio
I think you can create class called ResultProducts with all properties(same data type in the original table (nullable also need to put)) what you want to get. Then you can return that object.
public class ResultProducts
{
public int Quantity { get; set; }
public string ProductDesc { get; set; }
public int RemainingQuantity { get; set; }
public int QtyInHand { get; set; }
}
public IQueryable<ResultProducts> GetProductsByShipID(int id)
{
var oppProductss =from c in db.OpportunityProducts
from p in db.Products
where p.ProductID == c.ProductID
select new ResultProducts()
{
Quantity =c.Quantity,
ProductDesc= c.ProductDesc,
RemainingQuantity=c.RemainingQuantity,
QtyInHand=p.QtyInHand
};
return oppProductss ;
}
I hope this will work.

Map lists of nested objects with Dapper

I'm using Dapper and I have classes like this:
public class Article{
public int Id { get; set; }
public string Description{get;set;}
public Group Group { get; set; }
public List<Barcode> Barcode {get;set;}
...
}
public class Group{
public int Id { get; set; }
public string Description {get;set;}
}
public class Barcode{
public int Id { get; set; }
public string Code{get;set;}
public int IdArticle { get; set; }
...
}
I can get all information about Article but I would like to know if is possible with one query get also the list of barcodes for each article. Actually what I do is this:
string query = "SELECT * FROM Article a " +
"LEFT JOIN Groups g ON a.IdGroup = g.Id ";
arts = connection.Query<Article, Group, Article>(query,
(art, gr) =>
{ art.Group = gr; return art; }
, null, transaction).AsList();
I also found a good explanation here but I don't understand how to use it in my case, because I have also the Group class.
How should I do this with Dapper, is it possible or the only way is to do different steps?
Thanks
QueryMultiple is your friend
var query = #"
select a.*, g.* from Article a left join Groups g on g.Id = a.IdGroup
select * from Barcode";
//NOTE: IdGroup should exists in your Article class.
IEnumerable<Article> articles = null;
using (var multi = connection.QueryMultiple(query)){
articles = multi.Read<Article, Group, Article>((a, g)=>
{ a.Group = g; return a; });
if (articles != null) {
var barcodes = multi.Read<Barcode>().ToList();
foreach(var article in articles){
article.Barcode = barcodes.Where(x=>x.IdArticle == article.Id).ToList();
}
}
}
That may not be fun especially if you don't have any filters in your query. But I doubt that you will return all Articles. In that case you can filter the Barcode like this (edited sql) > select * from Barcode where Id in #ids. Then include the parameter ids (a list of Article Ids) in the QueryMultiple.
Option2
Or you could just do separate queries:
var query = "select a.*, g.* from Article a left join Groups g on g.Id = a.IdGroup";
var articles = connection.Query<Article, Group, Article>(query,
(a,g)=> { a.Group = g; return g; }).ToList();
query = "select * from Barcode where IdArticle IN #articleIds";
var articleIds = articles.Select(x=>x.Id);
var barcodes = connection.Query<Barcode>(query, new { articleIds });
foreach(var article in articles){
article.Barcode = barcodes.Where(x=>x.IdArticle == article.Id);
}
I prefer the first option.

Left join multiple IList<> using Linq

i have basically a post repository that should return all the gallery items belong to it. If there's no gallery belonging to post it should still return post distinct by post id
public List<PostLocalizedOutput> GetAllPostsWithCategories(string culture, bool? isPublished)
{
var query =
from p in Context.Posts
join pl in Context.PostsLocalized on p.Id equals pl.PostId
from c in p.Categories
join cl in Context.CategoriesLocalized on c.Id equals cl.CategoryId
from g in p.Galleries.DefaultIfEmpty()
join gi in Context.GalleryItems on g.Id equals gi.GalleryId
where
pl.Culture == culture &&
cl.Culture == culture
select new PostLocalizedOutput
{
PostId = pl.PostId,
CategoryId = cl.CategoryId,
Title = pl.Title,
FormattedCategoryName = cl.FormattedCategoryName,
PostContent = pl.PostContent,
PostType = pl.Post.PostType,
IOrder = pl.Post.IOrder,
Tags = pl.Tags,
PublishDate = pl.Post.PublishDate,
ViewCount = pl.Post.ViewCount,
ShowInHomePageSlider = pl.Post.ShowInHomePageSlider,
AllowComments = pl.Post.AllowComments,
Image = pl.Post.Image,
IsArchived = pl.Post.IsArchived,
IsDraft = pl.Post.IsDraft,
IsPublished = pl.Post.IsPublished,
GalleryItems = new GalleryItemOutput
{
FileName = gi.FileName,
GalleryId = gi.GalleryId,
Id = gi.Id,
Notes = gi.Notes,
Title = gi.Title
} (around here i feel like i should foreach something or what?)
};
return query.OrderBy(x => x.IOrder).ThenBy(x => x.PublishDate).DistinctBy(x => x.PostId).ToList();
}
here is my postlocalizedoutput
public class PostLocalizedOutput : IOutputDto
{
public int PostId { get; set; }
public int CategoryId { get; set; }
public bool IsPublished { get; set; }
...
public List<GalleryItemOutput> GalleryItems { get; set; }
}
GalleryItemOutput should be list because i want all the galleryitems of a post. But when i define it as a list in repository i cannot set each field of galleryitem of a post. This code now returns me four rows because i have four gallery items of that post and each one has the same postId. I do not want that. DefaultIfEmpty also does not work even if a post does not have any gallery items i should still be able to get that post without gallery items.
Any approach ?
Thanks for all suggestions.
This should help you get started.
Don’t know if this compiles, please just interpret as pseudo-code. Looks like your top-level range variable is PostsLocalized not Posts. This code assume/uses navigation properties that are probably setup in your EDM classes. I have overused let keyword here just to make it clearer for you. In the select new clause you can change the p1.Post.... to just p. I left them to make as few edits as possible.
var query =
from pl in Context.PostsLocalized
where pl.Culture == culture
let p = p1.Post
let categories = p.Categories
let localizedCategories = categories.SelectMany(cat => cat.CategoriesLocalized).Where(cl => cl.Culture == culture)
let galleries = p.Galleries
let galleryItems = galleries.SelectMany(gal => gal.GalleryItems)
let cl = localizedCategories.FirstOrDefault() // only one or zero of these i assume?
select new PostLocalizedOutput
{
PostId = p1.PostId,
CategoryId = cl.CategoryId,
Title = pl.Title,
FormattedCategoryName = cl.FormattedCategoryName,
PostContent = pl.PostContent,
PostType = pl.Post.PostType,
IOrder = pl.Post.IOrder,
Tags = pl.Tags,
PublishDate = pl.Post.PublishDate,
ViewCount = pl.Post.ViewCount,
ShowInHomePageSlider = pl.Post.ShowInHomePageSlider,
AllowComments = pl.Post.AllowComments,
Image = pl.Post.Image,
IsArchived = pl.Post.IsArchived,
IsDraft = pl.Post.IsDraft,
IsPublished = pl.Post.IsPublished,
GalleryItems = galleryItems.Select(gi => new GalleryItemOutput
{
FileName = gi.FileName,
GalleryId = gi.GalleryId,
Id = gi.Id,
Notes = gi.Notes,
Title = gi.Title
})
// might need a .ToList() here on those GalleryItems
(around here i feel like i should foreach something or what?)
};

How to populate `ICollection<T>` property inside a `List<T>` linq

My Class
public partial class CTITLE_CHECKLIST : CError
{
public int Id { get; set; }
[Required(ErrorMessage = "Requerido")]
public int ID_Tipo_Checklist { get; set; }
[Required(ErrorMessage = "Requerido")]
public string Descripcion { get; set; }
public virtual CTipo_CheckList Tipo_CheckList { get; set; }
public ICollection<CSUBTITLE_CHECKLIST> Subtitulos { get; set; }
}
My method :
public List<CTITLE_CHECKLIST> GetCheked(string codigo, int tipoCheckList)
{
try
{
var result = (from a in db.TITLE_CHECKLIST
from t in db.Tipo_CheckList
where a.ID_Tipo_Checklist == t.ID
where a.ID_Tipo_Checklist == tipoCheckList
select new CTITLE_CHECKLIST
{
Descripcion = a.Descripcion,
Id = a.Id,
ID_Tipo_Checklist = a.ID_Tipo_Checklist,
Tipo_CheckList = new CTipo_CheckList
{
Descripcion = t.Descripcion,
ID = t.ID,
ID_Depto = t.ID_Depto
},
Subtitulos = (from s in db.SUBTITLE_CHECKLIST
where s.ID_Title == a.Id
select new CSUBTITLE_CHECKLIST
{
AmountCK = s.AmountCK,
Descripcion = s.Descripcion,
ID = s.ID,
ID_Title = s.ID_Title,
Numeracion = s.Numeracion,
checkList = (from ck in db.CheckList
where ck.ID_Subtitle == s.ID
&& ck.Codigo == codigo
select new CCheckList
{
CK = ck.CK,
Amount = ck.Amount,
Codigo = ck.Codigo,
Codigo_TFile = ck.Codigo_TFile,
Comentario = ck.Comentario,
ID = ck.ID,
ID_Subtitle = ck.ID_Subtitle,
UserCre = ck.UserCre,
UserMod = ck.UserMod
}).FirstOrDefault()
})//here I put ToList
}).ToList();
An my error this :
Error 2 Cannot implicitly convert type 'System.Linq.IQueryable<TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST>' to 'System.Collections.Generic.ICollection<TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST>'. An explicit conversion exists (are you missing a cast?) C:\Users\jmitchell\Documents\Visual Studio 2012\Projects\TROP\TROP\Areas\TRAFICO\Controllers\LOGICA\CTITLE_CHECKLIST.cs 136 49 TROP
And When I put .ToList, where say Here I put ToList it throw an error that say :
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST] ToList[CSUBTITLE_CHECKLIST](System.Collections.Generic.IEnumerable`1[TROP.Areas.TRAFICO.Controllers.LOGICA.CSUBTITLE_CHECKLIST])' method, and this method cannot be translated into a store expression.
And I Know that if I change the property subtitles to IEnumerable<T> it will work, but, Im trying to bind this model from MVC4 post, And I doesn't bind with IEnumerable<T>, it bind with a ICollection<T>, List<T> , I am like 1 and a half day trying to figure out this problem.
So the choices are not "do the whole thing in the database" or "do the whole thing in memory". You want to do some of both. Do everything on the DB end that you can, and then when you've done all of that, finish up the remaining operations in memory. This general pattern (mostly leveraging AsEnumerable) will allow you to do this:
(from a in db.TITLE_CHECKLIST
from t in db.Tipo_CheckList
where a.ID_Tipo_Checklist == t.ID
where a.ID_Tipo_Checklist == tipoCheckList
select new //note were using an anonymous type here,
//as the real type can't take a non-list
{
Descripcion = a.Descripcion,
Id = a.Id,
ID_Tipo_Checklist = a.ID_Tipo_Checklist,
Tipo_CheckList = new CTipo_CheckList
{
Descripcion = t.Descripcion,
ID = t.ID,
ID_Depto = t.ID_Depto
},
Subtitulos = from s in db.SUBTITLE_CHECKLIST
where s.ID_Title == a.Id
select new CSUBTITLE_CHECKLIST
{
AmountCK = s.AmountCK,
Descripcion = s.Descripcion,
ID = s.ID,
ID_Title = s.ID_Title,
Numeracion = s.Numeracion,
checkList = (from ck in db.CheckList
where ck.ID_Subtitle == s.ID
&& ck.Codigo == codigo
select new CCheckList
{
CK = ck.CK,
Amount = ck.Amount,
Codigo = ck.Codigo,
Codigo_TFile = ck.Codigo_TFile,
Comentario = ck.Comentario,
ID = ck.ID,
ID_Subtitle = ck.ID_Subtitle,
UserCre = ck.UserCre,
UserMod = ck.UserMod
}).FirstOrDefault()
}//note no ToList
})
//This will ensure that all operators that follow
//are done in memory, not on the DB end
.AsEnumerable()
.Select(checklist => new CTITLE_CHECKLIST
{
Descripcion = checklist.Descripcion,
Id = checklist.Id,
ID_Tipo_Checklist = checklist.ID_Tipo_Checklist,
Subtitulos = Subtitulos.ToList(),
});

Union in entity framework

I have two tables: Vehicles and Workers.
Vehicle(Id, Number)
Workers(Id, Name, ContractorVehicleNumber)
I would like to write lambda query to return all the vehicles and the contractor vehicles. Something like in sql:
SELECT Id, Number
FROM Vehicle
UNION
SELECT NULL, ContractorVehicleNumber
FROM Workers
This is what I made:
public IQueryable<Vehicle> Get(bool includeContractorVehicles)
{
IQueryable<Vehicle> query = GetQuery();
if (includeContractorVehicles == true)
{
WorkerRepository rep = new WorkerRepository();
IQueryable<Vehicle> contractorsVehicles = rep.GetWirkers().
Select(x => new Vehicle()
{
VehicleNumber = x.ContractorVehicleNumber
});
query = query.Union(contractorsVehicles);
}
return query;
}
But I get an exception:
The entity or complex type 'XXXXXXXX' cannot be constructed in a LINQ to Entities query.
You cannot construct mapped entity type in projection. Your former example will work only if you create a new special type used for projection:
public class VehicleResult
{
public string Number { get; set; }
... // If you don't need more then one column you can use simple type instead of custom class
}
And your method will look like:
public IQueryable<VehicleResult> Get(bool includeContractorVehicles)
{
IQueryable<VehicleResult> query = GetQuery().Select(v => new VehicleResult { ... });
if (includeContractorVehicles == true)
{
WorkerRepository rep = new WorkerRepository();
IQueryable<VehicleResult> contractorsVehicles = rep.GetWorkers().
Select(x => new VehicleResult()
{
Number = x.ContractorVehicleNumber
});
query = query.Union(contractorsVehicles);
}
return query;
}
You cant create entities in the select statement. Try this instead:
public class VehicleDTO
{
public int Id { get; set; }
public int Number { get; set; }
}
public IQueryable<VehicleDTO> Get(bool includeContractorVehicles)
{
var query = GetQuery().Select(x => new VehicleDTO(){ ID = c.ID, Number = c.Number });
if (includeContractorVehicles)
{
WorkerRepository rep = new WorkerRepository();
var contractorsVehicles = rep.GetWirkers().
Select(x => new VehicleDTO(){ Number = x.ContractorVehicleNumber});
query = query.Union(contractorsVehicles);
}
return query;
}
Also are you sure you want a Union and not a Concat ?

Categories

Resources