So I have set the model here:
public class Stock
{
[Key]
//[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid StockId { get; set; } = Guid.NewGuid();
.....
public decimal Remaining { get; set; }
}
Also with fluent mapping:
protected override void OnModelCreating(DbModelBuilder mb)
{
base.OnModelCreating(mb);
mb.HasDefaultSchema("dbo");
mb.Entity<Stock>().Property(m => m.Remaining).HasPrecision(16, 3);
....
}
So the precision is 3 decimal points.
It is so weird when I querying with linq:
IQueryable<StockDetail> stocks = (from s in db.Stocks .... where s.Remaining > 0);
stocks.Any() --> is false;
The where s.Remaining > 0 is the problem, how to fix this?
EDIT
The actual query:
IQueryable<StockDetail> stocks = (from s in db.Stocks
join items in db.Items on s.Item equals items.ItemId
join types in db.ItemTypes on s.ItemType equals types.ItemTypeId
join colors in db.ItemColors on s.ItemColor equals colors.ItemColorId
join units in db.ItemUnits on s.Unit equals units.ItemUnitId
join buyers in db.Buyers on new { Buyer = s.Buyer } equals new { Buyer = (Guid?)buyers.BuyerId } into temp2
from buyers in temp2.DefaultIfEmpty(null)
join suppliers in db.Suppliers.DefaultIfEmpty() on new { Supplier = s.Supplier } equals new { Supplier = (Guid?)suppliers.SupplierId } into temp3
from suppliers in temp3.DefaultIfEmpty(null)
join op in db.UserProfiles on s.Operator equals op.UserId
join curr in db.Currencies on s.Currency equals curr.CurrencyId
let sales = db.Sales.Where(m => m.SId == s.Sales).FirstOrDefault()
join parent in db.Stocks on s.Parent equals parent.StockId
where s.Remaining > 0
select new StockDetail()
{
Buyer = buyers != null ? (Guid?)buyers.BuyerId : null,
BuyerName = buyers != null ? buyers.Name : null,
Supplier = suppliers.SupplierId,
SupplierName = suppliers.Name,
Code = s.Code,
Color = colors.ItemColorId,
ColorCode = colors.Code,
ColorName = colors.Color,
DateCreated = s.DateCreated,
Gramation = s.Gramation,
Item = items.ItemId,
ItemName = items.Name,
LastEdited = s.LastEdited,
Operator = op.UserId,
OperatorName = op.UserName,
PO = s.PO,
Remaining = s.Remaining,
SC = s.SC,
Qty = s.Qty,
Setting = s.ItemSetting,
StockId = s.StockId,
Type = types.ItemTypeId,
TypeName = types.Type,
Unit = units.ItemUnitId,
UnitName = units.Unit,
Lot = s.Lot,
AvgPrice = s.AvgPrice,
Currency = curr.CurrencyName,
CurrencyId = s.Currency,
Note = s.Note,
Purchase = s.Purchase,
Sales = s.Sales,
POIn = s.POIn,
FromFactory = s.FromFactory,
OutDeliveryNo = sales != null ? sales.InvoiceNo : "",
Spec = s.Spec,
DesignCode = s.DesignCode
});
Related
I have a database (PostgreSQL) where there is a main table student, additional information amount and 3 dictionaries. I make a query with grouping by three fields of dictionary IDs, output the number of objects and the amount from an additional table with a condition. And how to translate it to EF Core 6?
create table region (id serial primary key, name varchar);
create table district (id serial primary key, name varchar);
create table department (id serial primary key, name varchar);
create table student (
id serial primary key,
name varchar,
region_id bigint references region,
district_id bigint references district,
department_id bigint references department
);
create table amount (
id serial primary key,
student_id bigint references student on delete cascade,
value numeric,
year int
);
My SQL query is working well:
select
t.region_id,
region."name" region_name,
t.district_id,
district."name" district_name,
t.department_id,
department."name" department_name,
t.cnt,
t.value
from (
select
region_id,
district_id,
department_id,
count(distinct s.id) cnt,
sum(a.value) "value"
from student s
join amount a on s.id = a.student_id
where a.year = 2020
group by region_id, district_id, department_id
) t
join region on t.region_id = region.id
join district on t.district_id = district.id
join department on t.department_id = department.id
How do I get names from dictionaries when translating a query to EF?
[Table("student")]
public class Student
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string? Name { get; set; }
[Column("region_id")]
public int? RegionId { get; set; }
[Column("district_id")]
public int? DistrictId { get; set; }
[Column("department_id")]
public int? DepartmentId { get; set; }
[ForeignKey(nameof(RegionId))]
public virtual Region? Region { get; set; }
[ForeignKey(nameof(DistrictId))]
public virtual District? District { get; set; }
[ForeignKey(nameof(DepartmentId))]
public virtual Department? Department { get; set; }
public ICollection<Amount>? Amounts { get; set; }
}
EF query:
var result = await db.Student
.GroupBy(x => new { x.RegionId, x.DistrictId, x.DepartmentId })
.Select(x => new
{
x.Key.RegionId,
x.Key.DistrictId,
x.Key.DepartmentId,
Cnt = x.Count(),
Value = x.Sum(c => c.Amounts.Where(v => v.Year == 2020).Sum(v => v.Value))
})
.ToListAsync();
At the moment I have such a solution, but will such a request be optimal in the end? In addition, you need to add a null check here.
RegionName = x.First().Region.Name,
DistrictName = x.First().District.Name,
DepartmentName = x.First().Department.Name,
This can be done with the following EF Core query:
var query = from student in db.Student
join region in db.Region on student.RegionId equals region.id
join district in db.District on student.DistrictId equals district.id
join department in db.Department on student.DepartmentId equals department.id
join amount in db.Amount on student.Id equals amount.student_id
where amount.Year == 2020
group amount by new
{
student.RegionId,
RegionName = region.Name,
student.DistrictId,
DistrictName = district.Name,
student.DepartmentId,
DepartmentName = department.Name
} into g
select new
{
g.Key.RegionName,
g.Key.DistrictName,
g.Key.DepartmentName,
Cnt = g.Count(),
Value = g.Sum(a => a.Value)
};
var result = await query.ToListAsync();
It is translated into the following SQL:
SELECT r.name AS "RegionName", d.name AS "DistrictName", d0.name AS "DepartmentName",
count(*)::int AS "Cnt", COALESCE(sum(a.value), 0.0) AS "Value"
FROM student AS s
INNER JOIN region AS r ON s.region_id = r.id
INNER JOIN district AS d ON s.district_id = d.id
INNER JOIN department AS d0 ON s.department_id = d0.id
INNER JOIN amount AS a ON s.id = a.student_id
WHERE a.year = 2020
GROUP BY s.region_id, r.name, s.district_id, d.name, s.department_id, d0.name
If you need LEFT JOIN then it will be:
var query = from student in db.Student
join region in db.Region on student.RegionId equals region.id into rg
from r in rg.DefaultIfEmpty()
join district in db.District on student.DistrictId equals district.id into dg
from d in dg.DefaultIfEmpty()
join department in db.Department on student.DepartmentId equals department.id into dpg
from dp in dpg.DefaultIfEmpty()
join amount in db.Amount on student.Id equals amount.student_id
where amount.Year == 2020
group amount by new
{
student.RegionId,
RegionName = r.Name,
student.DistrictId,
DistrictName = d.Name,
student.DepartmentId,
DepartmentName = dp.Name
} into g
select new
{
g.Key.RegionName,
g.Key.DistrictName,
g.Key.DepartmentName,
Cnt = g.Count(),
Value = g.Sum(a => a.Value)
};
Try the following query:
var query =
from s in db.Student
from a in s.Amounts
where a.Year == 2020
group a by new
{
s.RegionId,
RegionName = s.Region.Name,
s.DistrictId,
DistrictName = s.District.Name,
s.DepartmentId,
DepartmentName = s.Department.Name
} into g
select new
{
x.Key.RegionId,
x.Key.DepartmentName,
x.Key.DistrictId,
x.Key.DistrictName,
x.Key.DepartmentId,
x.Key.DepartmentName,
Cnt = x.Select(v => v.StudentId).Distinct().Count(),
Value = x.Sum(v => v.Value)
};
var result = await query.ToListAsync();
Not sure that Cnt = x.Select(v => v.StudentId).Distinct().Count() will be translated, it depends on EF Core version.
UPDATE - added equivalent to the SQL query:
var groupingQuery =
from s in db.Student
from a in s.Amounts
where a.Year == 2020
group a by new
{
s.RegionId,
s.DistrictId,
s.DepartmentId,
} into g
select new
{
x.Key.RegionId,
x.Key.DistrictId,
x.Key.DepartmentId,
Cnt = x.Select(v => v.StudentId).Distinct().Count(),
Value = x.Sum(v => v.Value)
};
var query =
from g in groupingQuery
join region in db.Region on g.RegionId equals region.id
join district in db.District on g.DistrictId equals district.id
join department in db.Department on g.DepartmentId equals department.id
select new
{
g.RegionId,
RegionName = region.Name,
g.DistrictId,
DistrictName = district.Name,
g.DepartmentId,
DepartmentName = department.Name,
g.Cnt,
g.Value
};
var result = await query.ToListAsync();
So I have this line:
var transrevmax = db.TRANSACTs
.Where(x => x.SITE == "1" &&
x.DATE_IN >= dateinchoice &&
x.DATE_OUT <= dateoutchoice)
.GroupBy(x => x.TICKET_NO)
.Select(x => x.OrderByDescending(y => y.REV_NO).FirstOrDefault())
.ToList();
and it returns the exact list of transactions I want the join below to go through to obtain values from...
var exportrecovery = (from trans in transrevmax
join detail in db.DETAILs on new { TICKET_NO = trans.TICKET_NO, REV_NO = trans.REV_NO } equals new { TICKET_NO = detail.TICKET_NO, REV_NO = detail.REV_NO }
join job in db.JOBs on new { JOB_CODE = trans.JOB_CODE, CUST_CODE = trans.CUST_CODE } equals new { JOB_CODE = job.CODE, CUST_CODE = job.CUST_CODE }
join customer in db.CUSTOMERs on trans.CUST_CODE equals customer.CODE
join invoiced in db.INVOICEDs on trans.TICKET_NO equals invoiced.TICKET_NO
where trans.DATE_IN >= dateinchoice && trans.DATE_OUT <= dateoutchoice && trans.STATUS.ToString().Trim() != "V" && trans.STATUS.ToString().Trim() != "E"
select new { ADDRESS = customer.ADDRESS, CITY = customer.CITY, STATE = customer.STATE, ZIP = customer.ZIP, FREIGHT = detail.HAUL_CHGE + detail.FUEL_CHGE, JOB_NAME = job.NAME, HAUL_TAX = detail.HAUL_TAX, INVOICE_NO = invoiced.INVOICE_NO, CUST_NAME = customer.NAME, TAX_CODE = customer.TAX_CODE, MAT_CHGE = detail.MAT_CHGE, MAT_TAX = detail.MAT_TAX, MAT_CODE = detail.MAT_CODE, QTY = detail.QTY, PRICE = detail.PRICE, DATE_MOD = trans.DATE_OUT, REV_NO = trans.REV_NO, SITE = trans.SITE, TICKET_NO = trans.TICKET_NO, CUST_CODE = trans.CUST_CODE, JOB_CODE = trans.JOB_CODE }
).ToList();
... if I run the first line I get the transactions limited to only those with the max revision (what I want), and if I run the join (replacing the "transrevmax" with the table "db.TRANSACTs") I get the right range of ticket numbers, but it includes all revisions. I am stumped as to how I can use the joined tables and get only unique rows according to their (maximum) revision number. When these two are used in conjunction I receive zero rows. Please advise.
Remove the ToList on the transrevmax creation.
What I have:
Two lists of the following model:
int SubscriptionId
int ItemId
double Usage
double EffectiveRate
string ResourceName
string UnitOfMeasure
The first contains usage data of the last month like this:
SubscriptionId ItemId Usage EffectiveRate ResourceName UnitOfMesaure
_________________________________________________________________________
1 1 2 2,75 R1 U1
1 2 3 1,50 R2 U2
The seconds contains usage data of the current month like this:
SubscriptionId ItemId Usage EffectiveRate ResourceName UnitOfMesaure
_________________________________________________________________________
1 1 5 2,75 R1 U1
1 3 2 1,50 R3 U3
What I want:
This should be merge in a list like this:
SubscriptionId ItemId UsageThis UsageLast EffRate ResName UOM
_________________________________________________________________________
1 1 5 2 2,75 R1 U1
1 2 0 3 1,50 R2 U2
1 3 2 0 1,50 R3 U3
What I have:
//data for both months available
if (resourcesThisMonth.Any() && resourcesLastMonth.Any())
{
//join both months
resources = from resourceLastMonth in resourcesLastMonth
join resourceThisMonth in resourcesThisMonth
on new { resourceLastMonth.SubscriptionId, resourceLastMonth.ItemId } equals new { resourceThisMonth.SubscriptionId, resourceThisMonth.ItemId }
select new Resource
{
SubscriptionId = resourceThisMonth.SubscriptionId,
ItemId = resourceThisMonth.ItemId,
UsageThisMonth = resourceThisMonth.Usage,
UsageLastMonth = resourceLastMonth.Usage,
EffectiveRate = resourceThisMonth.EffectiveRate,
ResourceName = resourceThisMonth.ResourceName,
UnitOfMeasure = resourceThisMonth.UnitOfMeasure
};
//resources only last month available
var resourcesOnlyLastMonth = resourcesLastMonth.Where(r => !resourcesThisMonth.Where(s => s.ItemId == r.ItemId && s.SubscriptionId == r.SubscriptionId).Any())
.Select(r => new Resource
{
SubscriptionId = r.SubscriptionId,
ItemId = r.ItemId,
UsageThisMonth = 0.0,
UsageLastMonth = r.Units,
EffectiveRate = r.EffectiveRate,
ResourceName = r.ResourceName,
UnitOfMeasure = r.UnitOfMeasure
});
//resources only this month available
var resourcesOnlyThisMonth = resourcesThisMonth.Where(r => !resourcesLastMonth.Where(s => s.ItemId == r.ItemId && s.SubscriptionId == r.SubscriptionId).Any())
.Select(r => new Resource
{
SubscriptionId = r.SubscriptionId,
ItemId = r.ItemId,
UsageThisMonth = r.Usage,
UsageLastMonth = 0.0,
EffectiveRate = r.EffectiveRate,
ResourceName = r.ResourceName,
UnitOfMeasure = r.UnitOfMeasure
});
//union data
resources = resources.Union(resourcesOnlyLastMonth);
resources = resources.Union(resourcesOnlyThisMonth);
}
//data for last month available
else if (resourcesLastMonth.Any())
{
resources = from resource in resourcesLastMonth
select new Resource
{
SubscriptionId = resource.SubscriptionId,
ItemId = resource.ItemId,
UsageThisMonth = 0.0,
UsageLastMonth = resource.Usage,
EffectiveRate = resource.EffectiveRate,
ResourceName = resource.ResourceName,
UnitOfMeasure = resource.UnitOfMeasure
};
}
//data for this month available
else if (resourcesThisMonth.Any())
{
resources = from resource in resourcesThisMonth
select new Resource
{
SubscriptionId = resource.SubscriptionId,
ItemId = resource.ItemId,
UsageThisMonth = resource.Usage,
UsageLastMonth = 0.0,
EffectiveRate = resource.EffectiveRate,
ResourceName = resource.ResourceName,
UnitOfMeasure = resource.UnitOfMeasure
};
}
//no data available
else
{
resources = new List<Resource>();
}
Problem:
This is very much code - should be less, any possible solutions failed so far
Thanks for helping!
public class ExampleClass
{
public int Id1 { get; set; }
public int Id2 { get; set; }
public int Usage { get; set; }
public int UsageThis { get; set; }
public int UsageLast { get; set; }
}
List<ExampleClass> listThisMonth = new List<ExampleClass>
{
new ExampleClass{Id1=1, Id2=1,Usage=7, UsageThis=1, UsageLast=0},
new ExampleClass{Id1=2, Id2=2,Usage=4, UsageThis=2, UsageLast=0},
new ExampleClass{Id1=3, Id2=3,Usage=1, UsageThis=3, UsageLast=0},
};
List<ExampleClass> listLastMonth = new List<ExampleClass>
{
new ExampleClass{Id1=1, Id2=1,Usage=3, UsageThis=1, UsageLast=1},
new ExampleClass{Id1=4, Id2=4,Usage=3, UsageThis=4, UsageLast=3},
new ExampleClass{Id1=2, Id2=2,Usage=1, UsageThis=8, UsageLast=6},
};
var result = listThisMonth.Select(a=>new {value=a, list=1})
.Union(listLastMonth.Select(a => new { value = a, list = 2 }))
.GroupBy(a => new { Id1 = a.value.Id1, Id2 = a.value.Id2 })
.Select(x => new ExampleClass
{
Id1 = x.Key.Id1,
Id2 = x.Key.Id2,
UsageThis = x.Any(o => o.list == 1) ? x.First(o => o.list == 1).value.Usage : 0,
UsageLast = x.Any(o => o.list == 2) ? x.First(o => o.list == 2).value.Usage : 0,
Usage = x.Sum(o=>o.value.Usage)
}).ToList();
//id1 id2 current last sum
//1 1 7 3 10
//2 2 4 1 5
//3 3 1 0 1
//4 4 0 3 3
It looks to me that what you're looking for is a full outer join. Unfortunately, it looks like LINQ doesn't have a construct for that. So, there are a few options: LINQ - Full Outer Join
For your scenario, it looks like you have some redundant code. You should be able to do the union using two outer joins to get the correct result set. For example:
// Left join the current month with the last month
var currentMonth =
from current in resourcesThisMonth
join last in resourcesLastMonth on new { current.SubscriptionId, current.ItemId } equals new { last.SubscriptionId, last.ItemId } into outer
from o in outer.DefaultIfEmpty()
select new Resource
{
SubscriptionId = current.SubscriptionId,
ItemId = current.ItemId,
UnitsThisMonth = current.Units,
UnitsLastMonth = o?.Units ?? 0, // Replace NULL with 0
EffectiveRate = current.EffectiveRate,
ResourceName = current.ResourceName,
UnitOfMeasure = current.UnitOfMeasure
};
// Reverse of the first join. Last month LEFT JOIN Current month
var lastMonth =
from last in resourcesLastMonth
join current in resourcesThisMonth on new { last.SubscriptionId, last.ItemId } equals new { current.SubscriptionId, current.ItemId } into outer
from o in outer.DefaultIfEmpty()
select new Resource
{
SubscriptionId = last.SubscriptionId,
ItemId = last.ItemId,
UnitsThisMonth = o?.Units ?? 0, // Replace NULL with 0
UnitsLastMonth = last.Units,
EffectiveRate = o?.EffectiveRate ?? last.EffectiveRate,
ResourceName = o?.ResourceName ?? last.ResourceName,
UnitOfMeasure = o?.UnitOfMeasure ?? last.UnitOfMeasure
};
// Union them together to get a full join
var resources = currentMonth.Union(lastMonth);
I have this query
return from fitUpExaminationDetails in _ctx.FitUpExaminationDetails
where fitUpExaminationDetails.FitUpExaminationId == Id
join joint in _ctx.Joints on fitUpExaminationDetails.JointId equals joint.Id
join line in _ctx.Lines on joint.LineId equals line.Id
join fileIsoManager in _ctx.FileIsoManagers on line.Id equals fileIsoManager.LineId
select new ViewDomainClass.QualityControl.Report.ViewFitupExaminationReport
{
HeatNumber1 = fitUpExaminationDetails.HeatNumber1,
HeatNumber2 = fitUpExaminationDetails.HeatNumber2,
JointNumber = joint.JointNumber,
LineNumber = line.LineNumber,
Revision = fileIsoManager.Revision,
};
My line tables can have multi fileIsoManager records.So in the select statement i mean in this part Revision = fileIsoManager.Revision i want to show the latest record in fileIsoManager table .how can i change this query to do that ?
Ok, since you didn't provide much details, i've wrote a few classes to show you how to get your latest revision for each line. Pay attention to the "main" function.
// These are a representation of your classes :
public class Line
{
public int LineId { get; set; }
public string SomeValue { get; set;}
}
public class Revision
{
public int RevisionId { get; set; }
public int LineId { get; set;}
}
void Main()
{
// generating some data so we can test the query.
var lines = new List<Line>() {
new Line() { LineId = 1, SomeValue = "Allo!" }
};
var revisions = new List<Revision>() {
new Revision() { LineId = 1, RevisionId = 1 },
new Revision() { LineId = 1, RevisionId = 2 },
new Revision() { LineId = 1, RevisionId = 3 }
};
var result = (
from line in lines
join revision in revisions on line.LineId equals revision.LineId
group revision by line into grp
select new
{
Line = grp.Key,
LastRevision = grp.OrderByDescending(rev => rev.RevisionId).FirstOrDefault()
}
).ToList();
}
So, basically, you need to group your revisions by line. In the select part, you can then get the latest revision for each line.
In this example, result will contain a dynamic object containing the line id=1 and the revision id = 3.
Going back to your query, it should look like this to get a similar result :
return
from fitUpExaminationDetails in _ctx.FitUpExaminationDetails
where fitUpExaminationDetails.FitUpExaminationId == Id
join joint in _ctx.Joints on fitUpExaminationDetails.JointId equals joint.Id
join line in _ctx.Lines on joint.LineId equals line.Id
join fileIsoManager in _ctx.FileIsoManagers on line.Id equals fileIsoManager.LineId
group fileIsoManager by new { fitUpExaminationDetails, joint, line} into grp
select new ViewDomainClass.QualityControl.Report.ViewFitupExaminationReport
{
HeatNumber1 = grp.Key.fitUpExaminationDetails.HeatNumber1,
HeatNumber2 = grp.Key.fitUpExaminationDetails.HeatNumber2,
JointNumber = grp.Key.joint.JointNumber,
LineNumber = grp.Key.line.LineNumber,
Revision = grp.OrderByDescending(fileIsoMgr => gileIsoMgr.Id).FirstOrDefault()
};
How can i get the rows that have null in the column used for self join ?
My code is :
IEnumerable<ListClassView> list = from l in lists join lp in lists on
l.ParentListId equals lp.Id
select new ListClassView()
{
Id = l.Id,
ListName = l.ListName,
Description = l.Description,
ParentName = lp.ListName,
IsActive = l.IsActive
};
I am unable to fetch rows where ParentListId=null. Is there a way where i can get all rows ?
Alternative syntax:
var list = lists.GroupJoin(
lists,
l => l.ParentListId,
lp => lp.Id,
(l, lp) => new ListClassView
{
Id = l.Id,
ListName = l.ListName,
Description = l.Description,
ParentName = lp.FirstOrDefault() == null ? null : lp.First().ListName,
IsActive = l.IsActive
});
I found the solution from here by applying a left join :
IEnumerable<ListClassView> list = from l in lists join lp in lists on
l.ParentListId equals lp.Id into lpp
from p in lpp.DefaultIfEmpty()
select new ListClassView()
{
Id = l.Id,
ListName = l.ListName,
Description = l.Description,
ParentName = p.ListName,
IsActive = l.IsActive
};
Now, It gets all rows including those with ParentListId = null