ASP.Net MVC converting Sql to Linq - linq

I'm updating an old app, to use EF and Linq. I'm having trouble with one of the queries - in SQL it is:
SELECT id, type_id, rule_name, rule_start, rule_end, rule_min
FROM Rules
WHERE (rule_min > 0)
AND (rule_active = 1)
AND (rule_fri = 1)
AND ('2012-01-01' BETWEEN rule_start AND rule_end)
AND (id IN
(SELECT rule_id
FROM RulesApply
WHERE (type_id = 3059)))
ORDER BY pri
So far I have:
var rules = db.Rules.Include("RulesApply")
.Where(t => (t.rule_active == 1)
&& (t.rule_min > 0)
&& (dteFrom >= t.rule_start && dteFrom <= t.rule_end)
&& (this is where I'm stuck)
)
.OrderBy(r => r.pri);
It's the last subquery I'm stuck with adding into the LINQ above:
AND (id IN
(SELECT rule_id
FROM RulesApply
WHERE (type_id = 3059)))
Models are:
public class Rule
{
[Key]
public Int64 id { get; set; }
public Int64 hotel_id { get; set; }
public byte rule_active { get; set; }
public DateTime rule_start { get; set; }
public DateTime rule_end { get; set; }
public int rule_min { get; set; }
public int pri { get; set; }
public virtual ICollection<RuleApply> RulesApply { get; set; }
}
public class RuleApply
{
[Key, Column(Order = 0)]
public Int64 type_id { get; set; }
[Key, Column(Order = 1)]
public Int64 rule_id { get; set; }
[ForeignKey("rule_id")]
public virtual Rule Rule { get; set; }
}
Can anyone please help me complete this query?
Thank you,
Mark

Try doing this:
var rules = db.Rules.Include("RulesApply")
.Where(t => (t.rule_active == 1)
&& (t.rule_min > 0)
&& (dteFrom >= t.rule_start && dteFrom <= t.rule_end)
&& t.RulesApply.Any(a => a.type_id == 3059)
.OrderBy(r => r.pri);
If t.RulesApply is illegal (i.e. doesn't compile), then replace it with the correct reference to the navigation property found on your Rules object that points to the RulesApply object.

If you have set up navigational properties between the entities, you can navigate from one to the other:
//This gets the RulesApply object
var rulesapply = db.RulesApply.Single(x=> x.type_id == 3059);
//This gets all Rules connected to the rulesapply object through its navigational property
var rules = rulesapply.Rules;
//You can use LINQ to further refine what you want
rules = rules.Where( x=> /* and so on...*/ );
You can stack these statements together on a single line, I only split them up for readability purposes :)

Related

SQL query with Group By and Having Clause in LINQ structure

how do I write this query in LINQ (c# EF6)?
Can someone please help me here - I am new to Entity Framework Structure - So bit hard for me to use different clauses
SELECT
sum(remainingamount) TotalSiteCreditAmount,
max(expirationutcdatetime) MaxExpiryDate
FROM
WholesaleCredits
WHERE
ExpirationUTCDateTime > Getdate()
GROUP BY
registeredcustomerid,
siteid
HAVING
registeredcustomerid = :registeredCustomerId
AND siteid = :siteId
Tried below thing as of now :
var data = context.WholesaleCredit
.Where(x => x.ExpirationUTCDateTime > DateTime.Now)
.GroupBy (x => x.RegisteredCustomerId)
Entity Used in code:
public partial class WholesaleCredits
{
public virtual int Id { get; set; }
public virtual decimal CreditAmount { get; set; }
public virtual decimal RemainingAmount { get; set; }
public virtual Site Site { get; set; }
public virtual DateTime GeneratedUTCDateTime { get; set; }
public virtual DateTime ExpirationUTCDateTime { get; set; }
public virtual int RegisteredCustomerId { get; set; }
}
You do not need HAVING here and Grouping should be provided by constant, because you have filter on grouping keys:
var data = context.WholesaleCredit
.Where(x => x.ExpirationUTCDateTime > DateTime.Now && x.RegisteredCustomerId == registeredCustomerId && x.Site.Id == siteid)
.GroupBy(x => 1)
.Select(g => new
{
TotalSiteCreditAmount = g.Sum(x => x.RemainingAmount),
MaxExpiryDate = g.Max(x => x.ExpirationUTCDateTime)
})
.First();

Selecting single element from each group that cointains maximum DateTime value

model :
public class ReferenceParameterHistory
{
[Key]
public int IDReferenceParameterHistory { get; set; }
public double Value { get; set; }
public string Value_S { get; set; }
public DateTimeOffset CreatedAt { get; set; }
[Required]
public bool IsStable { get; set; }
public int? IDReference { get; set; }
public Reference Reference { get; set; }
[Required]
public int IDParameterTemplate { get; set; }
public ParameterTemplate ParameterTemplate { get; set; }
}
My code in ASP.NET core controller :
[HttpGet]
public async Task<IActionResult> GetReferenceParameterHistory(int? IDparameterTemplate,
int? IDreference,
DateTimeOffset? startDate,
DateTimeOffset? endDate,
bool latestOnly)
{
try
{
IQueryable<ReferenceParameterHistory> query = _context.ReferenceParameterHistory.OrderByDescending(rph => rph.IDReferenceParameterHistory);
if (IDparameterTemplate != null && IDparameterTemplate > 0)
query = query.Where(rph => rph.IDParameterTemplate == IDparameterTemplate);
if (IDreference != null && IDreference > 0)
query = query.Where(rph => rph.IDReference == IDreference);
if (startDate != null)
query = query.Where(rph=> rph.CreatedAt >= startDate);
if (endDate != null)
query = query.Where(rph => rph.CreatedAt <= endDate);
if (latestOnly)
{
// I tried this but it doesnt compile and I don't have idea how to solve this ....
//query = (from rph in query
// group rph by rph.IDParameterTemplate
// into groups
// where groups.Max(rph => rph.CreatedAt)
// select groups.Key);
}
var referenceParameterHistory = await query.AsNoTracking().ToListAsync();
if (referenceParameterHistory.Any())
return new ObjectResult(referenceParameterHistory);
return new NotFoundResult();
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return new StatusCodeResult(500);
}
}
I have a database table based on that ReferenceParameterHistory model class. I want to group records exctracted from that table by IDParameterTemplate and from each group I need to extract records that have the highest value in CreatedAt column (latest records). So each group contains many recods but I need to get only these with max value in CreatedAt column. The result should be IEnumerable of ReferenceParameterHistory since I store that query in an IQueryable variable and then send it to SQL Server to process the query. Commented code in my example is just what I tried but I don't know how to do that.
How can I solve that problem ?
You reused the variable rph inside the lambda.
How to select only the records with the highest date in LINQ
query = (from rph in query
group rph by rph.IDParameterTemplate into g
select g.OrderByDescending(t=>t.CreatedAt).FirstOrDefault());

Dynamic LINQ: Comparing Nested Data With Parent Property

I've a class with following structure:
public class BestWayContext
{
public Preference Preference { get; set; }
public DateTime DueDate { get; set; }
public List<ServiceRate> ServiceRate { get; set; }
}
public class ServiceRate
{
public int Id { get; set; }
public string Carrier { get; set; }
public string Service { get; set; }
public decimal Rate { get; set; }
public DateTime DeliveryDate { get; set; }
}
and I've dynamic linq expression string
"Preference != null && ServiceRate.Any(Carrier == Preference.Carrier)"
and I want to convert above string in Dynamic LINQ as follows:
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<BestWayContext, bool>(condition, null).Compile();
But it showing following error:
Please correct me what am I doing wrong?
It looks like you wanted to do something like this:
var bwc = new BestWayContext
{
Preference = new Preference { Carrier = "test" },
DueDate = DateTime.Now,
ServiceRate = new List<ServiceRate>
{
new ServiceRate
{
Carrier = "test",
DeliveryDate = DateTime.Now,
Id = 2,
Rate = 100,
Service = "testService"
}
}
};
string condition = "Preference != null && ServiceRate.Any(Carrier == #0)";
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<BestWayContext, bool>(condition, bwc.Preference.Carrier).Compile();
bool res = expression(bwc); // true
bwc.ServiceRate.First().Carrier = "test1"; // just for testing this -> there is only one so I've used first
res = expression(bwc); // false
You want to use Preference which belong to BestWayContext but you didn't tell the compiler about that. If i write your expression on Linq i will do as follows:
[List of BestWayContext].Where(f => f.Preference != null && f.ServiceRate.Where(g => g.Carrier == f.Preference.Carrier)
);
As you see i specified to use Preference of BestWayContext.

Adding where and sum to lambda expression

I have the below Linq query:
var qry = from Output in db.Outputs
join ShiftHours in db.ShiftHourses on Output.ShiftHour equals ShiftHours.ShiftHour
join ShiftData in db.ShiftDatas on Output.ShiftID equals ShiftData.ShiftID
where ShiftData.ShiftDate == date && ShiftData.Line == line
select new ProgressData()
{
CPM = ShiftData.CPM,
Target = ShiftData.Target,
CurrentOutput = db.Outputs.Sum(x=>x.Quantity),
PercentOfTarget = (db.Outputs.Sum(x=>x.Quantity) / ShiftData.Target) * 100
};
It is almost doing what I want but as it stands, the CurrentOutput lambda expression is returning the sum of the entire Quantity column of the Output table as I am unsure how to add in a 'Where' clause as well as the sum function (and hence the PercentOfTarget is also incorrect).
The where clause needs to be the same as the first where clause (date and line are parameters passed to the method):
where ShiftData.ShiftDate == date && ShiftData.Line == line
Can anyone help?
EDIT: Clarification of CurrentOutput.
In the 'Output' table there can be multiple records for a given 'ShiftData.ShiftDate' and 'ShiftData.Line' combination so I would like to calculate a sum of the 'Output' table 'Quantity' column values for a specified 'ShiftDate' and 'Line'
EDIT: Further clarification
This is some sample data from the Output table (OutputID is an auto-increment PK):
public class Output
{
[Key]
public int OutputId { get; set; }
public int ShiftID { get; set; }
public int Quantity { get; set; }
public int ShiftHour { get; set; }
public virtual ShiftData ShiftData { get; set; }
}
This is some sample data from the ShiftData table (ShiftID is an auto-increment PK, there will be more than one record for each date as further line numbers are added):
public class ShiftData
{
[Key]
public int ShiftID { get; set; }
public DateTime ShiftDate { get; set; }
public string Line { get; set; }
public int CPM { get; set; }
public double Target { get; set; }
}
So using the above sample data, I am trying to populate a ProgressData object:
public class ProgressData
{
public int CPM { get; set; }
public double Target { get; set; }
public int CurrentOutput { get; set; }
public double PercentOfTarget { get; set; }
}
Based on the sample data, I would expect my ProgressData object created for line 1 on 13/2/2014 to be populated as such:
CPM = 5, Target = 200, CurrentOutput = 120, PercentOfTarget = 60
You can try to do group join for that purpose :
var qry = from ShiftData in db.ShiftDatas
join Output in db.Outputs on ShiftData.ShiftID equals Output.ShiftID
into ShiftGroup
where ShiftData.ShiftDate == date && ShiftData.Line == line
select new ProgressData()
{
CPM = ShiftData.CPM,
Target = ShiftData.Target,
CurrentOutput = ShiftGroup.Sum(x=>x.Quantity),
PercentOfTarget = (ShiftGroup.Sum(x=>x.Quantity) / ShiftData.Target) * 100
};
Another thing, I can't see why you need to do join with ShiftHours here, since none of it's property used in select statement.
Just as #har07 posted I managed to get it working using the below. I am posting this for reference as it does answer the original question but I'm going to try and use #har07's code as it's tidier than mine.
var qry = (from Output in db.Outputs
join ShiftHours in db.ShiftHourses on Output.ShiftHour equals ShiftHours.ShiftHour
join ShiftData in db.ShiftDatas on Output.ShiftID equals ShiftData.ShiftID
where ShiftData.ShiftDate == date && ShiftData.Line == line
select new
{
ShiftData.ShiftDate,
ShiftData.Line,
ShiftData.CPM,
ShiftData.Target,
Output.Quantity
}).ToList();
var progress = qry.GroupBy(l => l.ShiftDate).Select(g => new ProgressData()
{
CPM = g.Where(c => c.ShiftDate == date && c.Line == line).Select(c => c.CPM).FirstOrDefault(),
Target = g.Where(c => c.ShiftDate == date && c.Line == line).Select(c => c.Target).FirstOrDefault(),
CurrentOutput = g.Where(c => c.ShiftDate == date && c.Line == line).Sum(c => c.Quantity),
PercentOfTarget = g.Where(c => c.ShiftDate == date && c.Line == line).Sum(c => (c.Quantity / c.Target) * 100)
});
return progress.FirstOrDefault();

Linq Many to Many query

I need help to query this three tables. RentCommunityFeature and RentPropertyFeature has a many to many relationship with RentUnitListing. My problem is i can't get these three tables to query. What i want is all those rentlistings that has certain features. for example if RentCommunityFeature has a "pool" and RentPropertyFeature has a "parking", i want all the records in RentUnitListing that has "Pool" and "Parking". If no parking than result should show only with "Pool".
I tried the below query but it gives incorrect results. It shows duplicate results when myCommunityFeatureId or myPropertyFeatureId is -1. I have initializes them to -1 if they are empty in DB.
Any help would be greatly appreciated.
var AllAds = from r in _db.RentUnitListings
from cf in r.RentCommunityFeatures
from pf in r.RentPropertyFeatures
where (myCommunityFeatureId > 0) ? (cf.RentCommunityFeatureID == myCommunityFeatureId && cf.RentUnitListings.) : (myCommunityFeatureId == -1)
where (myPropertyFeatureId > 0) ? (pf.RentPropertyFeatureID == myPropertyFeatureId) : (myPropertyFeatureId == -1)
select r;
public partial class RentCommunityFeature
{
public int RentCommunityFeatureID { get; set; }
public string RentCommunityFeatureDescription { get; set; }
public virtual ICollection<RentUnitListing> RentUnitListings { get; set; }
}
public partial class RentPropertyFeature
{
public int RentPropertyFeatureID { get; set; }
public string RentPropertyFeatureDescription { get; set; }
public virtual ICollection<RentUnitListing> RentUnitListings { get; set; }
}
public partial class RentUnitListing
{
public Guid RentUnitListingID { get; set; }
public string RentUnitListingShortDescription { get; set; }
public virtual ICollection<RentCommunityFeature> RentCommunityFeatures { get; set; }
public virtual ICollection<RentPropertyFeature> RentPropertyFeatures { get; set; }
}
var listings = _db.RentUnitListings
.Where(rul => rul.RentCommunityFeatures
.Any(rcf => rcf.RentCommunityFeatureID == myCommunityFeatureId)
|| rul.RentPropertyFeatures
.Any(rpf => rpf.RentPropertyFeatureID == myPropertyFeatureId))
.ToList();
It means: Return all listings that have at least one (Any) RentCommunityFeature with the myCommunityFeatureId OR at least one (Any) RentPropertyFeature with the myPropertyFeatureId. The "OR" is not exclusive, so a returned listing may have a "Pool" without a "Parking" feature or a "Parking" without a "Pool" feature or it might have both. In any case a returned listing might have a lot of other features in addition to "Pool" or "Parking".

Resources