I have a WCF Service Library and Widnows Form as a client. I have database ADO.NET EF
I want to list all of the products (clothes) with their sizes. (Relation 1 to many).
public partial class ProductsEntity
{
public ProductsEntity()
{
this.Sizes = new HashSet<SizesEntity>();
}
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual ICollection<SizesEntity> Sizes{ get; set; }
}
this is my data contract:
[DataContract]
public class Products
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name{ get; set; }
[DataMember]
public decimal Price { get; set; }
[DataMember]
public virtual ICollection<SizesEntity> Sizes{ get; set; }
}
[DataContract]
public class Sizes
{
[DataMember]
public int ID { get; set; }
[DataMember]
public int Name { get; set; }
[DataMember]
public Nullable<int> Quantity { get; set; }
[DataMember]
public int ID_Product { get; set; }
[DataMember]
public virtual ProductsEntity Products { get; set; }
}
i dont have this in data base, but i added Products_with_sizes for my query (Im not sure its a good way of dealing with it)
[DataContract]
public class Products_with_sizes
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal Price { get; set; }
[DataMember]
public int S { get; set; }
[DataMember]
public int M { get; set; }
[DataMember]
public int L { get; set; }
}
using (var context = new dbMagazynierEntities())
{
var q = (from p in context.Products
where p.Name.Contains(name) && p.Price>= Price_from && p.Price <= Price_to
join r in context.Sizes
on p.ID equals r.Prodcuts.ID
into sizes
select new
{
ID = p.ID,
Name= p.Name,
Price = p.Price,
S = sizes.Where(x => x.Name== 0).Sum(x => x.Quantity) ?? 0,
M = sizes.Where(x => x.Name== 1).Sum(x => x.Quantity) ?? 0,
L = sizes.Where(x => x.Name== 2).Sum(x => x.Quantity) ?? 0,
});
odp = new List<Products_with_sizes>();
foreach (var item in q)
{
odp.Add(new Products_with_sizes{ ID = item.ID, Name= item.Name, Price = item.Price, S = item.S, M = item.M, L = item.L });
}
so know I use this method in my client and i get error
wyn = context.SzukajProduktu(id, name.Text, price_from, price_to);
i get:
Cannot implicitly convert type 'System.Collections.Generic.List<Magazynier2WindowsFormsApplication.ServiceReference1.MyServiceProducts_with_sizes>' to 'System.Collections.Generic.List<Magazynier2ServiceLibrary.MyService.Products_with_sizes>'
By looking at your exception, it seems that you're trying to directly cast a class generated by your service proxy to the DTO you created yourself.
Even though those 2 classes have the same name and properties, they are in fact different (i.e. have no common parent or inteface) and are in a different namespace.
You should write a method that would translate the proxy generated class to your DTO class explicitely, e.g.
List<Magazynier2ServiceLibrary.MyService.Products_with_sizes> TranslateProxyClassToDTO(List<Magazynier2WindowsFormsApplication.ServiceReference1.MyServiceProducts_with_sizes> input)
{
// translate all items and their properties and return the translated list
}
public List<Prodcuts_with_sizes> SzukajProduktu(int id, string name, decimal price_from, decimal price_to)
{
List<Prodcuts_with_sizes> odp;
if (id == -1) //when id is not given
{
using (var context = new dbMagazynierEntities())
{
var q = (from p in context.Products
where p.Name.Contains(name) && p.Price >= price_from && p.Price <= price_to
join r in context.Size
on p.ID equals r.Products.ID
into sizes
select new
{
ID = p.ID,
Name = p.Name,
Price = p.Price,
S = sizes.Where(x => x.Name == 0).Sum(x => x.Quantity) ?? 0,
M = sizes.Where(x => x.Name == 1).Sum(x => x.Quantity) ?? 0,
L = sizes.Where(x => x.Name == 2).Sum(x => x.Quantity) ?? 0,
});
odp = new List<Prodcuts_with_sizes>();
foreach (var item in q)
{
odp.Add(new Prodcuts_with_sizes { ID = item.ID, Name = item.Name, Price = item.Price, S = item.S, M = item.M, L = item.L });
}
//dataGridView1.DataSource = q.ToList();
}
return odp;
}
else //when id is given
{
using (var context = new dbMagazynierEntities())
{
var q = (from p in context.Products
where p.ID == id
join r in context.Sizes
on p.ID equals r.Products.ID
into sizes
select new
{
ID = p.ID,
Name = p.Name,
Price = p.Price,
S = sizes.Where(x => x.Name == 0).Sum(x => x.Quantity) ?? 0,
M = sizes.Where(x => x.Name == 1).Sum(x => x.Quantity) ?? 0,
L = sizes.Where(x => x.Name == 2).Sum(x => x.Quantity) ?? 0,
});
odp = new List<Prodcuts_with_sizes>();
foreach (var item in q)
{
odp.Add(new Prodcuts_with_sizes { ID = item.ID, Name = item.Name, Price = item.Price, S = item.S, M = item.M, L = item.L });
}
}
return odp;
}
}
using (var context = new MyInterfaceClient())
{
wyn = context.SzukajProduktu(id, name.Text, price_from, price_to);
//return wyn;
}
I resolved it by changing
[OperationContract]
List<WCFLIB.MyService.Products_with_sizes> SzukajProduktu(int id, string name, decimal price_form, decimal price_to);
to
[OperationContract]
List<MyService.Products_with_sizes> SzukajProduktu(int id, string name, decimal price_form, decimal price_to);
Related
Given the following code example, how do I:
Get the commented out lines in the unfiltered list to work (without changing the definition of Result)?
Get the commented out lines in the filtered list to work (without changing the definition of Result)? From my maths it should give 32 records. Hopefully my output intent is clear enough for others to understand
Any questions feel free to ask
Regards
Kyle
//Populate data
var alphas = new List<Alpha>();
for (int a = 1; a <= 10; a++)
{
var alpha = new Alpha() { Id = a, Name = "A" + a };
for (int b = 1; b <= 10; b++)
{
var beta = new Beta() { Id = b, Name = "B" + b };
for (int c = 1; c <= 10; c++)
{
var charlie = new Charlie() { Id = c, Name = "C" + c };
for (int d = 1; d <= 10; d++)
{
var delta = new Delta() { Id = d, Name = "D" + d };
charlie.Deltas.Add(delta);
}
beta.Charlies.Add(charlie);
}
alpha.Betas.Add(beta);
}
alphas.Add(alpha);
}
//Get results into required format without filtering
var unfilteredResults = alphas.Select(a => new Result
{
AId = a.Id,
AName = a.Name,
//BId = a.Betas.Select(b => b.Id),
//BName = a.Betas.Select(b => b.Name),
//CId = a.Betas.Select(b => b.Charlies.Select(c => c.Id)),
//CName = a.Betas.Select(b => b.Charlies.Select(c => c.Name)),
//DId = a.Betas.Select(b => b.Charlies.Select(c => c.Deltas.Select(d => d.Id))),
//DName = a.Betas.Select(b => b.Charlies.Select(c => c.Deltas.Select(d => d.Name)))
});
var whiteListAIds = new List<int>() { 1, 2 };
var whiteListBIds = new List<int>() { 3, 4 };
var whiteListCIds = new List<int>() { 5, 6 };
var whiteListDIds = new List<int>() { 7, 8 };
//Get results into required format with filtering
var filteredResults = alphas.Where(a => whiteListAIds.Contains(a.Id)).Select(a => new Result
{
AId = a.Id,
AName = a.Name,
//BId = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Id),
//BName = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Name),
//CId = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Id)),
//CName = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Name)),
//DId = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Deltas.Where(d => whiteListDIds.Contains(d.Id)).Select(d => d.Id))),
//DName = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Deltas.Where(d => whiteListDIds.Contains(d.Id)).Select(d => d.Name)))
});
class Alpha
{
public int Id { get; set; }
public string Name { get; set; }
public List<Beta> Betas { get; set; } = new List<Beta>();
}
class Beta
{
public int Id { get; set; }
public string Name { get; set; }
public List<Charlie> Charlies { get; set; } = new List<Charlie>();
}
class Charlie
{
public int Id { get; set; }
public string Name { get; set; }
public List<Delta> Deltas { get; set; } = new List<Delta>();
}
class Delta
{
public int Id { get; set; }
public string Name { get; set; }
}
class Result
{
public int AId { get; set; }
public string AName { get; set; }
public int BId { get; set; }
public string BName { get; set; }
public int CId { get; set; }
public string CName { get; set; }
public int DId { get; set; }
public string DName { get; set; }
}
Got it working as below thanks to the first answer linq selectmany flatten multiple levels
Basically had to combine .SelectMany() on the "outer" parents and .Select() on the last/inner child.
//Populate data
var alphas = new List<Alpha>();
for (int a = 1; a <= 10; a++)
{
var alpha = new Alpha() { Id = a, Name = "A" + a };
for (int b = 1; b <= 10; b++)
{
var beta = new Beta() { Id = b, Name = "B" + b };
for (int c = 1; c <= 10; c++)
{
var charlie = new Charlie() { Id = c, Name = "C" + c };
for (int d = 1; d <= 10; d++)
{
var delta = new Delta() { Id = d, Name = "D" + d };
charlie.Deltas.Add(delta);
}
beta.Charlies.Add(charlie);
}
alpha.Betas.Add(beta);
}
alphas.Add(alpha);
}
var unfilteredResults = alphas.SelectMany(a => a.Betas.SelectMany(b=> b.Charlies.SelectMany(c=> c.Deltas.Select(d => new Result
{
AId = a.Id,
AName = a.Name,
BId = b.Id,
BName = b.Name,
CId = c.Id,
CName = c.Name,
DId = d.Id,
DName = d.Name
}))));
var whiteListAIds = new List<int>() { 1, 2 };
var whiteListBIds = new List<int>() { 3, 4 };
var whiteListCIds = new List<int>() { 5, 6 };
var whiteListDIds = new List<int>() { 7, 8 };
//Get results into required format with filtering
var filteredResults = unfilteredResults.Where(r => whiteListAIds.Contains(r.AId) && whiteListBIds.Contains(r.BId) && whiteListCIds.Contains(r.CId) && whiteListDIds.Contains(r.DId));
Console.WriteLine("Finished");
class Alpha
{
public int Id { get; set; }
public string Name { get; set; }
public List<Beta> Betas { get; set; } = new List<Beta>();
}
class Beta
{
public int Id { get; set; }
public string Name { get; set; }
public List<Charlie> Charlies { get; set; } = new List<Charlie>();
}
class Charlie
{
public int Id { get; set; }
public string Name { get; set; }
public List<Delta> Deltas { get; set; } = new List<Delta>();
}
class Delta
{
public int Id { get; set; }
public string Name { get; set; }
}
class Result
{
public int AId { get; set; }
public string AName { get; set; }
public int BId { get; set; }
public string BName { get; set; }
public int CId { get; set; }
public string CName { get; set; }
public int DId { get; set; }
public string DName { get; set; }
}
Hell sir/mam
this is my raw query
SELECT
dbo.Products.AHPPartnerId,
dbo.Products.Name AS Product,
dbo.AHPPartners.Name AS Partner,
Count(dbo.OrderProducts.ProductId) AS totalCount,
dbo.Products.Id AS ProductId
FROM
dbo.AHPPartners
RIGHT JOIN dbo.Products ON dbo.Products.AHPPartnerId = dbo.AHPPartners.Id
RIGHT JOIN dbo.OrderProducts ON dbo.OrderProducts.ProductId = dbo.Products.Id
GROUP BY
dbo.AHPPartners.Id,
dbo.Products.AHPPartnerId,
dbo.Products.Name,
dbo.AHPPartners.Name,
dbo.Products.Id
ORDER BY
totalCount DESC
how could i transfer this to lambda expression i wanna target it here in my DTO?
{
public long ProductId { get; set; }
public long? PartnerId { get; set; }
public string PartnerName { get; set; }
public string ProductName { get; set; }
public double TotalCount { get; set; }
}
Hello I got an answer already.
var topseller = await Context.Entities.Products
.GroupJoin(Context.Entities.AHPPartners,
products => products.AHPPartnerId,
ahp => ahp.Id,
(products, ahp) => ahp.Select(s => new { p = products, a = s }).DefaultIfEmpty(new { p = products, a = (AHPPartner)null })
).SelectMany(g => g)
.Join(Context.Entities.OrderProducts,
firstJoin => firstJoin.p.Id,
orderProducts => orderProducts.ProductId,
(firstJoin, orderProducts) => new { firstJoin.p, firstJoin.a, orderProducts }
)
.Select(s => new {
ProductName = s.p.Name,
ProductId = s.p.Id,
PartnerName = s.a.Name,
PartnerId = s.a.Id
})
.GroupBy(g => new { g.PartnerName, g.ProductName, g.ProductId, g.PartnerId })
.Select(s => new TopSellerProductDTO {
ProductName = s.Key.ProductName,
PartnerName = s.Key.PartnerName,
PartnerId = s.Key.PartnerId,
ProductId = s.Key.ProductId,
TotalCount = s.LongCount()
})
.ToListAsync();
I am using .NET Core 2.2, EF Core, C# and SQL Server 2017.
I am not able to translate the query I need to Linq.
This is the query I need to convert:
SELECT TOP 5
p.Id,
p.Title,
AVG(q.RatingValue) AvgRating
FROM Movies AS p
INNER JOIN Ratings AS q ON p.Id = q.MovieId
GROUP BY p.Id, p.Title
ORDER BY AvgRating DESC, p.Title ASC
The idea of the previous query is to get the Top 5 movies according to the Avg rating, ordering it by the highest average first, and in case of same average order alphabetically.
So far this is my query that makes the join, but then still missing: the group by, average, and ordering:
public class MovieRepository : IMovieRepository
{
private readonly MovieDbContext _moviesDbContext;
public MovieRepository(MovieDbContext moviesDbContext)
{
_moviesDbContext = moviesDbContext;
}
public IEnumerable<Movie> GetTopFive()
{
var result = _moviesDbContext.Movies.OrderByDescending(x => x.Id).Take(5).
Include(x => x.Ratings);
return result;
}
}
And these are the entities:
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public int YearOfRelease { get; set; }
public string Genre { get; set; }
public int RunningTime { get; set; }
public IList<Rating> Ratings { get; set; }
}
public class Rating
{
public int Id { get; set; }
public int MovieId { get; set; }
public int UserId { get; set; }
public decimal RatingValue { get; set; }
}
I tried to use Linqer tool also to convert my query to Linq, but it was not working.
I will appreciate any help to convert that query to LINQ for the method "GetTopFive".
Thanks
Try this one -
var data = _moviesDbContext.Movies.Include(x => x.Ratings)
.Select(x => new {
Id = x.Id,
Title = x.Title,
Average = (int?)x.Ratings.Average(y => y.RatingValue)
}).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();
Try as follows:
public IEnumerable<Movie> GetTopFive()
{
var result = _moviesDbContext.Ratings.GroupBy(r => r.MovieId).Select(group => new
{
MovieId = group.Key,
MovieTitle = group.Select(g => g.Movie.Title).FirstOrDefault(),
AvgRating = group.Average(g => g.RatingValue)
}).OrderByDescending(s => s.AvgRating).Take(5).ToList();
return result;
}
This will exclude the movies having no ratings.
But if you do as follows (as artista_14's answer):
public IEnumerable<Movie> GetTopFive()
{
var result = _moviesDbContext.Movies.GroupBy(x => new { x.Id, x.Title })
.Select(x => new {
Id = x.Key.Id,
Title = x.Key.Title,
Average = x.Average(y => y.Ratings.Sum(z => z.RatingValue))
}).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();
return result;
}
this will include the movies having no ratings also.
Note: I see your Rating model class does not contain any Movie navigation property. Please add this as follows:
public class Rating
{
public int Id { get; set; }
public int MovieId { get; set; }
public int UserId { get; set; }
public decimal RatingValue { get; set; }
public Movie Movie { get; set; }
}
and finally this is the code working nicely:
var data = _moviesDbContext.Movies.Include(x => x.Ratings)
.Select(x => new MovieRating
{
Id = x.Id,
Title = x.Title,
Average = x.Ratings.Average(y => y.RatingValue)
}).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();
return data;
The problem was creating an anonymous type in the select, so this line resolves the issue: .Select(x => new MovieRating
And this is the complete code for the method and the new class I have created to map the select fields with a concrete type:
public class MovieRepository : IMovieRepository
{
private readonly MovieDbContext _moviesDbContext;
public MovieRepository(MovieDbContext moviesDbContext)
{
_moviesDbContext = moviesDbContext;
}
public IEnumerable<Movie> GetAll()
{
return _moviesDbContext.Movies;
}
public IEnumerable<MovieRating> GetTopFive()
{
var result = _moviesDbContext.Movies.Include(x => x.Ratings)
.Select(x => new MovieRating
{
Id = x.Id,
Title = x.Title,
Average = x.Ratings.Average(y => y.RatingValue)
}).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();
return result;
}
}
public class MovieRating
{
public int Id { get; set; }
public string Title { get; set; }
public decimal Average { get; set; }
}
I donĀ“t know where is the mistake why it says that does not contain a defintion of ImporteSolicitado, interesesDemora and importeReintegro when they are colums of c and the last one of d
var importes = (from c in _context.ReintegroSolicitado
join d in _context.ReintegroRecibido on c.Expediente.ID equals d.Expediente.ID
group new {c,d} by new { c.Expediente.Codigo} into cd
select new { ImporteSolictadoFinal = cd.Sum(b => b.ImporteSolicitado + b.InteresesDemora), ImporteReintegroFinal = cd.Sum(e => e.ImporteReintegro) });
your group element contains two property c and d. So you need refer to
this property as
...
select new {
ImporteSolictadoFinal = cd.Sum(b => b.c.ImporteSolicitado + b.c.InteresesDemora),
ImporteReintegroFinal = cd.Sum(e => e.d.ImporteReintegro) }
...
This is very tough to get right with query posted. I did my best, but it is probably not exactly correct.
var importes = (from c in _context.reintegroSolicitado
join d in _context.reintegroRecibido on c.expediente.ID equals d.expediente.ID
select new { reintegroSolicitado = c, reintegroRecibido = c})
.GroupBy(x => new { c = x.reintegroSolicitado , d = x.reintegroRecibido})
.Select(cd => new { ImporteSolictadoFinal = cd.Sum(b => b.reintegroSolicitado.ImporteSolicitado + b.reintegroSolicitado.InteresesDemora), ImporteReintegroFinal = cd.Sum(e => e.reintegroRecibido.ImporteReintegro) });
}
}
public class Context
{
public List<ReintegroSolicitado> reintegroSolicitado { get; set; }
public List<ReintegroSolicitado> reintegroRecibido { get; set; }
public Expediente expediente { get; set; }
}
public class ReintegroSolicitado
{
public Expediente expediente { get; set; }
public int ImporteSolicitado { get; set; }
public int InteresesDemora { get; set; }
public int ImporteReintegro { get; set; }
}
public class Expediente
{
public int ID { get; set; }
public int Codigo { get; set; }
}
I have two tables in Database:
PostCalculationLine
PostCaluclationLineProduct
PostCalculationLineProduct(table2) contains Foriegn key of PostCalucationLineId(table1)
In C# code I have two different Models for these two tables as follows:
public class PostCalculationLine : BaseModel
{
public long Id{ get; set; }
public string Position { get; set; }
public virtual Order Order { get; set; }
public virtual Task Task { get; set; }
//some other properties go here
public virtual IList<PostCalculationLineProduct> PostCalculationLineProducts { get; set; }
}
and
public class PostCalculationLineProduct : BaseModel
{
public long Id {get;set;}
public string Description { get; set; }
//some other properties go here
}
Now in Entityframework code, I fetch data from PostCalculationLineProduct as follows:
PostCalculationLineRepository pclr = new PostCalculationLineRepository();
DataSourceResult dsrResult = pclr.Get()
.SelectMany(p => p.PostCalculationLineProducts)
.Where(c => c.Product.ProductType.Id == 1 && c.DeletedOn == null)
.Select(c => new HourGridViewModel()
{
Id = c.Id,
Date = c.From,
EmployeeName = c.Employee != null ?c.Employee.Name:string.Empty,
Description= c.Description,
ProductName = c.Product != null?c.Product.Name :string.Empty,
From = c.From,
To = c.Till,
Quantity = c.Amount,
LinkedTo = "OrderName",
Customer ="Customer"
PostCalculationLineId = ____________
})
.ToDataSourceResult(request);
In the above query I want to get PostCalculationLineId(from Table1) marked with underLine. How can I achieve this?
Thanks
You can use this overload of SelectMany to achieve this:-
DataSourceResult dsrResult = pclr.Get()
.SelectMany(p => p.PostCalculationLineProducts,
(PostCalculationLineProductObj,PostCalculationLineObj) =>
new { PostCalculationLineProductObj,PostCalculationLineObj })
.Where(c => c.PostCalculationLineProductObj.Product.ProductType.Id == 1
&& c.PostCalculationLineProductObj.DeletedOn == null)
.Select(c => new HourGridViewModel()
{
Id = c.PostCalculationLineProductObj.Id,
Date = c.PostCalculationLineProductObj.From,
//Other Columns here
PostCalculationLineId = c.PostCalculationLineObj.Id
};
This will flatten the PostCalculationLineProducts list and returns the flattened list combined with each PostCalculationLine element.