I have this error in my LINQ statement, however i dont understand whats the problem with it or how can i solve it:
This is my LINQ:
int year = 2016;
int month = 11;
DateTime dateFrom = new DateTime(year, month, 1);
DateTime dateTo = dateFrom.AddMonths(1);
int daysInMonth = DateTime.DaysInMonth(year, month);
var data = db_pdv.Pdv.Where(x => x.Fecha >= dateFrom && x.Fecha < dateTo);
var model_pdv = data.GroupBy(x => new { Pdv = x.Clave_PDV, Nombre_Pdv = x.Nombre_Pdv, Turno = x.Turno, Nombre_Turno = x.Nombre_Turno, Pla_ID = x.Platillo, Nombre_Platillo = x.Nombre_Platillo, Precio = x.Precio })
.Select(x => new DishVM()
{
Clave_PDV = x.Key.Pdv,
Nombre_Pdv = x.Key.Nombre_Pdv,
Turno = x.Key.Turno,
Nombre_Turno = x.Key.Nombre_Turno,
Platillo = x.Key.Pla_ID,
Nombre_Platillo = x.Key.Nombre_Platillo,
Precio = x.Key.Precio,
Days = new List<int>(new int[daysInMonth]),
Data = x
}).ToList();
And this is my "DishVM" CLass
public class DishVM
{
public string Clave_PDV { get; set; }
public string Nombre_Pdv { get; set; }
public string Turno { get; set; }
public string Nombre_Turno { get; set; }
public int Platillo { get; set; }
public string Nombre_Platillo { get; set; }
[DisplayFormat(DataFormatString = "{0:C}")]
public decimal Precio { get; set; }
public List<int> Days { get; set; }
[Display(Name = "Quantity")]
public int TotalQuantity { get; set; }
[DisplayFormat(DataFormatString = "{0:C}")]
[Display(Name = "Total")]
public decimal TotalPrice { get; set; }
public IEnumerable<Pdv> Data { get; set; }
}
How can i solve this problem?
Thanks in advance
How can I solve this problem?
Start by knowing what the problem is.
In Entity Framework, any expression that operates on a DbSet, like db_pdv.Pdv, is translated into SQL. The whole expression. In your case, this "whole expression" is model_pdv, which is structured as db_pdv.Pdv.Where(...).GroupBy(...).Select(). The expression contains new List<int>(new int[daysInMonth]). You'll understand that it's impossible to translate this into SQL; how would a database engine know how to construct a .Net List<T> object?
So how to solve it?
You could build the list first and then build the expression:
...
var daysList = new List<int>(new int[daysInMonth]);
var data = db_pdv.Pdv.Where(...
...
Precio = x.Key.Precio,
Days = daysList,
Data = x
Now you've reduced the SQL translation task to converting primitive values (integers) into SQL. EF knows perfectly well how to do that. But the result is... funny. If you check the generated SQL you see that EF converts the list of integers into some sort of a SQL table. Which looks like ...
CROSS JOIN (SELECT
0 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
0 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]
UNION ALL
SELECT
0 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable3]
UNION ALL
...
... etcetera.
What happens here, basically, is build a list in c#, convert it into a SQL construct, let the database construct a result set, convert the result set into a list in c# -- complicated.
Another option is to run the whole statement in memory:
var data = db_pdv.Pdv.Where(x => x.Fecha >= dateFrom && x.Fecha < dateTo)
.AsEnumerable();
var model_pdv = ...
Normally, I don't advocate this approach, for reasons explained here. But in this case it would be OK, because in the end you're going to use all data (Data = x), so you won't fetch more than you need from the database.
A more profound solution would be to remove the list from the view model altogether. Redundancy is the mother of inconsistency. Why should all model instances need the same list of integers? You should be able to build the list only once where you need it to present a UI.
Related
I have the following SQL Query:
SELECT
[nu_ano],
[nu_mes],
[id_projeto],
[id_fase],
'Financial Progress ("CompetĂȘncia")' as ds_categoria,
'Baseline' as ds_curva,
vl_baseline as vl_curva,
cast((vl_baseline / (pc_baseline / 100)) as decimal(18,2)) as vl_curva_total
FROM [Alvarez_Marsal].[dbo].[Schedule_Status]
And my LINQ query is like this:
var result = (from l in db.Schedule_Status
.Where(x => x.nu_mes == 12)
.Select(x => new Retorno
{
nu_ano = x.nu_ano,
nu_mes = x.nu_mes,
id_projeto = x.id_projeto,
id_fase = x.id_fase,
ds_categoria = "Financial Progress ('CompetĂȘncia')",
ds_curva = "Baseline",
vl_curva = x.vl_baseline,
vl_curva_total = decimal.Round((decimal)(x.vl_baseline / x.pc_baseline / 100), 2)
})
select l);
Transforming my LINQ query in SQL query, the result is:
SELECT
[Extent1].[nu_ano] AS [nu_ano],
[Extent1].[nu_mes] AS [nu_mes],
[Extent1].[id_projeto] AS [id_projeto],
[Extent1].[id_fase] AS [id_fase],
N'Financial Progress (''CompetĂȘncia'')' AS [C1],
N'Baseline' AS [C2],
[Extent1].[vl_baseline] AS [vl_baseline],
ROUND(([Extent1].[vl_baseline] / [Extent1].[pc_baseline]) / cast(100 as decimal(18)), 2) AS [C3]
FROM [dbo].[Schedule_Status] AS [Extent1]
WHERE 12 = [Extent1].[nu_mes]
Class Retorno
class Retorno
{
public int nu_ano { get; set; }
public short nu_mes { get; set; }
public int id_projeto { get; set; }
public int id_fase { get; set; }
public string ds_categoria { get; set; }
public string ds_curva { get; set; }
public decimal? vl_curva { get; set; }
public decimal? vl_curva_total { get; set; }
}
Given that there is no Convert.ToDecimal() in SQL language, how could I transcribe as decimal(18,2) in LINQ? is it possible? I have tried using decimal.Round() but it is not working.
The field vl_curva_total in my LINQ query is returning
3000.0000000000000000000 while correct would be to return 30000000.00
Your order of operations has changed between the two queries.
SQL Query
(vl_baseline / (pc_baseline / 100))
LINQ Query
(x.vl_baseline / x.pc_baseline / 100)
This explains the difference in the two values (3000 vs 30000000). Simply add the parentheses to your LINQ query to fix this.
(x.vl_baseline / (x.pc_baseline / 100))
Suppose I have a 2 table join in a function that returns an IQueryable, but the output is a named type that is neither of the two tables:
var qry = from p in Persons
join h in Hobbies on p.PersonId equals h.PersonId
select new OutputType
{
Name = p.FirstName,
Hobby = h.HobbyName
}
return qry
Let's say now I wanted to take this returned query and do something like:
var newQuery = qry.Where( p=>p.Age > 18 )
As you can see this is a problem because the IQueryable is of type OutputType, so I can't add a where to a person's age unless I were to add the Age to OutputType.
Is there anyway of 'breaking into' the IQueryable expression tree and adding a lambda somehow that will query on the source collection specified in it and add a Where clause to it? Or do I have do I have to add a Where field to the OutputType even though I'm uninterested in ultimately projecting it?
It is easier to narrow your view later than to try to backtrack. Here is a stripped down example of how I like to layer methods for reuse so that they spit out nice sql.
private IQueryable<Part> GetParts_Base()
{
//Proprietary. Replace with your own.
var context = ContextManager.GetDbContext();
var query = from c in context.Component
where c.Active
//kind of pointless to select into a new object without a join, but w/e
select new Part()
{
PartNumber = c.ComponentNumber,
Description = c.ComponentDescription,
Cost = c.ComponentCost,
Price = c.ComponentPrice
};
return query;
}
//Exclude cost from this view
public IEnumerable<Part_PublicView> GetParts_PublicView(decimal maxPrice)
{
var query = GetParts_Base();
var results = from p in query
where p.Cost < maxPrice
select new Part_PublicView()
{
PartNumber = p.PartNumber,
Description = p.Description,
Price = p.Price
};
return results;
}
public class Part_PublicView
{
public string PartNumber { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
}
private class Part : Part_PublicView
{
public decimal Cost { get; set; }
}
Linq-to-entity does not penalize you for selecting the extra column early on. As you can see, the sql includes the Cost column in the constraint but not in the select.
SELECT
1 AS [C1],
[Extent1].[ComponentNumber] AS [ComponentNumber],
[Extent1].[ComponentDescription] AS [ComponentDescription],
[Extent1].[ComponentPrice] AS [ComponentPrice]
FROM [dbo].[Component] AS [Extent1]
WHERE [Extent1].[ComponentCost] < #p__linq__0
So i want to achieve something like:
var query = from p in db.Project
select new A
{
Project = p,
Capacity = new List<Capacity>((from pp in db.ProjectActualCapacity
where pp.ProjectID == p.ID
select new Capacity
{
Actual = pp.Hours,
Date = pp.Date,
ProjectID = pp.ProjectID
}
).ToList())
};
However, when the query is converted to list. It throws the following error
Only parameterless constructors and initializers are supported in LINQ to Entities.
Is there a workaround to this?
thanks
//Update
public class Capacity
{
public DateTime Date { get; set; }
public decimal? Actual { get; set; }
public decimal? Projected { get; set; }
public int ProjectID { get; set; }
public decimal Rate { get; set; }
}
You are explicitly creating a list and using the constructor which accepts an enumerable. This is not necessary since you are already using .ToList() where you define that collection.
Also, your Capacity class needs a parameterless constructor.
So I think it will work like this.
var query = from p in db.Project
select new A {
Project = p,
Capacity = (from pp in db.ProjectActualCapacity
where pp.ProjectID == p.ID
select new Capacity {
Actual = pp.Hours,
Date = pp.Date,
ProjectID = pp.ProjectID
}
).ToList())
};
I have given up trying to create a linq query to retrieve a sql server view which is a union between two tables. I will now try to create a linq union.
I have two views, MemberDuesPaid and MemberDuesOwed. They have the same fields in both; (BatchNo, TranDate, DebitAmount, CreditAmount, ReceiptNo, CheckNo, SocSecNo).
I also have a helper class in my application which is called MemberTransaction. It has all the same properties.
How how do i do a union between the two tables where socSecNo = the ssn passed in? I want to union the two tables and return an IEnumerable collection of MemberTransaction. After the two tables are unioned together i want to have the collection that is returned ordered by trandate in descending order.
You can do it in a Linq Union query:
var infoQuery =
(from paid in db.MemberDuesPaid
select new MemberTransaction() {
BatchNo = paid.BatchNo,
TranDate = paid.TranDate,
DebitAmount = paid.DebitAmount,
CreditAmount = paid.CreditAmount,
ReceiptNo = paid.ReceiptNo,
CheckNo = paid.CheckNo,
SocSecNo = paid.SocSecNo})
.Union
(from owed in db.MemberDuesOwed
select new MemberTransaction() {
BatchNo = owed.BatchNo,
TranDate = owed.TranDate,
DebitAmount = owed.DebitAmount,
CreditAmount = owed.CreditAmount,
ReceiptNo = owed.ReceiptNo,
CheckNo = owed.CheckNo,
SocSecNo = owed.SocSecNo});
That should return you a set with everything combined into a single list.
[Edit]
If you want distinct values, you can do something like this after the above statement (you can do it inline if you bracket everything, but this is simpler to explain):
infoQuery = infoQuery.Distinct();
The variable infoQuery will by this time be populated entirely with objects of type MemberTransaction rather than the two disparate types in the union statement.
Assuming you've got two collections, one representing each view:
var paid = new List<MemberDuesPaid>();
var owed = new List<MemberDuesOwed>();
Convert both collections above to instances of the third class before performing the union:
var everyone
= paid.Select(x => new MemberTransaction { BatchNo = x.BatchNo, ... })
.Union(owed.Select(x => new MemberTransaction { BatchNo = x.BatchNo, ... }))
.Where(x => x.SocSecNo == ssn)
.OrderByDescending(x => x.TranDate)
.ToList();
Now you've got a collection of MemberTransaction, but there's nothing to indicate how one MemberTransaction equals another. So if you just run the above, you'll end up with everything from both collections in the result, instead of a true union.
You have to tell it what makes two instance equal, by implementing IEquatable<T> on the MemberTransaction class.
public class MemberTransaction : IEquatable<MemberTransaction>
{
public int BatchNo { get; set; }
public DateTime TranDate { get; set; }
public decimal DebitAmount { get; set; }
public decimal CreditAmount { get; set; }
public int ReceiptNo { get; set; }
public int CheckNo { get; set; }
public int SocSecNo { get; set; }
public bool Equals(MemberTransaction other)
{
return BatchNo == other.BatchNo
&& TranDate.Equals(other.TranDate)
&& DebitAmount == other.DebitAmount
&& CreditAmount == other.CreditAmount
&& ReceiptNo == other.ReceiptNo
&& CheckNo == other.CheckNo
&& SocSecNo == other.SocSecNo;
}
}
My problem is that when I have following 2 queries, 1st one is not populating the CampaignID property but 2nd one does. Here is my code;
query 1;
var query = from c in _context.MCTargets
where c.TargetDateFrom==d1 && c.TargetDateTo<=d2
group c by c.MarketingCampaignID into g
select new MSReport{
CampaignID = g.Key, // CampaignID is not populated here.
StartDate = d1,
EndDate = d2
};
query 2;
var query2 = from c in _context.MCTargets
where c.TargetDateFrom == d1 && c.TargetDateTo <= d2
group c by c.MarketingCampaignID into g
select new
{
CampaignID = g.Key,
StartDate = d1,
EndDate = d2
};
MSReport.cs
public class MSReport
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int CampaignID { get; set; }
public MSReport()
{
// CampaignID = 0 here
// doing something with CampaignID here like setting some calculated properties.
}
}
Thanks in advance and sorry for my poor explanation.
When using the object initializer syntax, the values specified in the initializer are set after the constructor for the object is executed. If you need the values that you are trying to populate to be available to the constructor, you must add a form of the constructor that takes the values as arguments and populates the fields or properties itself.
In your class:
public MSReport(int campaignID, DateTime startDate, DateTime endDate)
{
CampaignID = campaignID;
StartDate = startDate;
EndDate = endDate;
// doing something with CampaignID here like setting some calculated properties.
}
In your query:
new MSReport(g.Key, d1, d2)
This will work for Linq to SQL and Linq to Objects. For Linq to Entities a different approach must be taken.
You can execute the query with an anonymous object and then run a second query to transform it into your desired object:
var query = from c in _context.MCTargets
where c.TargetDateFrom==d1 && c.TargetDateTo<=d2
group c by c.MarketingCampaignID into g
select new {
CampaignID = g.Key,
StartDate = d1,
EndDate = d2
};
IEnumerable<MSReport> queryMSReports = from item in query.AsEnumerable()
select new MSReport(item.CampaignID, item.StartDate, item.EndDate);
This disconnects the object from Linq to Entities and allows you to create your desired objects with a constructor that has parameters. See the LINQ to Entites 'parameterless constructor' error forum post on MSDN for more information.
Your other option is to do the query using your MSReport class and the object initializer syntax then have a Calculate method on your class that you would have to call later.
Here is an example....
public class SimpleNameValueItem
{
public string Name { get; set; }
public Guid Uid { get; set; }
public int Id { get; set; }
public string Value { get; set; }
}
var shapeItems = from x in AppModel.ShapeTypes select new SimpleNameValueItem { Name = x.ShapeName, Uid = x.UID };
Where AppModel.ShapeTypes is entity of Entity Framework.
Maybe defalt constructor runs before initialization of params?
Try to add constructor in MSReport with your params and debug.
public class MSReport
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int CampaignID { get; set; }
public MSReport(int campaginId, ....)
{
// use and initialize camaginId here
}
}
and do:
select new MSReport(g.Key) {
StartDate = d1,
EndDate = d2
}