LINQ query to include the parent name in the child item - linq

public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public bool MarkAsPresent { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderItemId { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public Order Order { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
Please someone can help? From ASP.NET Core API controller, I need to return the following JSON. Please note that in the OrderItems, it includes the Product.ProductName. Also, if the Order.MarkAsPresent is true, then the productName should be replaced as 'Gift' in the JSON data.
{
"order": {
"orderId": "123",
"orderDate": "01-May-2021",
"orderItems": [
{ "productName": "Pen", "quantity": "25", "retailPrice": "3.50" },
{ "productName": "Paper", "quantity": "500", "retailPrice": "5.50" },
]
}
}

The two answers given so far are far too cumbersome. There's a very common, standard way to do this:
var result = from o in context.Order
select new
{
o.OrderId,
o.OrderDate,
OrderItems =
from oi in o.OrderItems
select new
{
ProductName = o.MarkAsPresent
? "Gift"
: oi.Product.ProductName,
oi.Quantity,
RetailPrice = oi.Price
}
};
You didn't define RetailPrice, so I assume it's just OrderItem.Price. If you like, the two select new statement can be replaced by projections into named DTO classes like OrderDto and OrderItemDto, respectively.

You can do it using an OrderDto.
public class OrderDto
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public object OrderItems { get; set; }
public OrderDto(int orderId, DateTime orderDate)
{
OrderId = orderId;
OrderDate = orderDate;
}
}//Cls
//--------------------------------------------//
public class OrderItemDto
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal RetailPrice { get; set; }
public OrderItemDto(string giftName, int quantity, decimal retailPrice)
{
ProductName = giftName;
Quantity = quantity;
RetailPrice = retailPrice;
}
}//Cls
//--------------------------------------------//
public class GiftDto
{
public string GiftName { get; set; }
public int Quantity { get; set; }
public decimal RetailPrice { get; set; }
public GiftDto(string giftName, int quantity, decimal retailPrice)
{
GiftName = giftName;
Quantity = quantity;
RetailPrice = retailPrice;
}
}//Cls
Add this method to your Order class
public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public bool MarkAsPresent { get; set; }
public List<OrderItem> OrderItems { get; set; }
public OrderDto ToDto()
{
var dto = new OrderDto(OrderId, OrderDate);
if (MarkAsPresent)
dto.OrderItems = OrderItems.Select(oi => new GiftDto(oi.Product.ProductName, oi.Quantity, oi.Price));
else
dto.OrderItems = OrderItems.Select(oi => new OrderItemDto(oi.Product.ProductName, oi.Quantity, oi.Price));
return dto;
}
}//Cls
Then in your action return something like Ok(order.ToDto());
Examples:
[HttpGet]
[AllowAnonymous]
public IActionResult OrderGift()
{
var order = new Order()
{
OrderId = 1,
OrderDate = DateTime.Now,
MarkAsPresent = true
};
order.OrderItems = new List<OrderItem>();
for (int i = 0; i < 5; i++)
{
order.OrderItems.Add(
new OrderItem()
{
Order = order,
OrderId = order.OrderId,
OrderItemId = i + 10,
Price = (i + 1) * 10,
Product = new Product()
{
ProductId = i + 20,
ProductName = $"Name {i}"
},
ProductId = i + 20,
Quantity = i * 2 + 4
});
}//for
return Ok(order.ToDto());
}///OrderGift
[HttpGet]
[AllowAnonymous]
public IActionResult OrderRegular()
{
var order = new Order()
{
OrderId = 1,
OrderDate = DateTime.Now,
MarkAsPresent = false
};
order.OrderItems = new List<OrderItem>();
for (int i = 0; i < 5; i++)
{
order.OrderItems.Add(
new OrderItem()
{
Order = order,
OrderId = order.OrderId,
OrderItemId = i + 10,
Price = (i + 1) * 10,
Product = new Product()
{
ProductId = i + 20,
ProductName = $"Name {i}"
},
ProductId = i + 20,
Quantity = i * 2 + 4
});
}//for
return Ok(order.ToDto());
}//OrderRegular

You can also use the Linq's let clause:
var report =
from o in orders
let orderItems =
from oi in o.OrderItems
select new {
productName = o.MarkAsPresent ? "Gift" : oi.Product.ProductName,
quantity = oi.Quantity,
retailPrice = oi.Price
}
select new {
orderId = o.OrderId,
orderDate = o.OrderDate,
orderItems
};

Use join to join the two models so we can connect every properties, and group all the
data by
OrderId and OrderDate, then just select the property you want to show.
About the gift,you can use if else in select.
Following is my demo:
[HttpGet]
public JsonResult Get()
{
var model = (from e in _context.OrderItems.Include(x=>x.Order).ToList()
join y in _context.Products.ToList() on e.OrderItemId equals y.ProductId
group new { e, y } by new { e.OrderId, e.Order.OrderDate } into g
select new
{
order = new
{
orderId = g.Key.OrderId,
orderDate = g.Key.OrderDate,
orderItems = (
from t in g.ToList()
select new
{
productName = t.e.Order.MarkAsPresent == true ? "Gift" : t.y.ProductName,
quantity = t.e.Quantity,
retailPrice = t.e.Price
}
).ToList()
}
}).ToList();
return new JsonResult(model);
}
Result:

Related

Linq not returning values when using Select to new Object

I am trying to get a list of objects out of a linq query. Below my classes
public partial class FuelBenefit
{
public int FuelBenefitId { get; set; }
public Nullable<int> EmployeeId { get; set; }
public Nullable<int> VehicleId { get; set; }
public Nullable<double> QuantityByCicle { get; set; }
public Nullable<int> UOMId { get; set; }
public Nullable<System.DateTime> DateStart { get; set; }
public Nullable<System.DateTime> DateEnd { get; set; }
public Nullable<bool> Active { get; set; }
public partial class FuelAmountEmployeeCycle
{
public int FuelAmountEmployeeCicleId { get; set; }
public Nullable<int> CycleId { get; set; }
public Nullable<int> EmployeeId { get; set; }
public Nullable<double> Amount { get; set; }
public Nullable<double> Balance { get; set; }
}
and Query. I don't get an error but no results.
var y = (from e in db.FuelBenefits
where e.Active == true
select new FuelAmountEmployeeCycle
{
CycleId = 1,
EmployeeId = e.EmployeeId,
Amount = e.QuantityByCicle,
Balance = e.QuantityByCicle
});
When i do this
var y = (from e in db.FuelBenefits
where e.Active == true
select e );
I do get results.
I have another project where I do this and it works, not sure why this time is not working.
Thank you!
var y = (from e in db.FuelBenefits
where e.Active == true
select new FuelAmountEmployeeCycle
{
CycleId = 1,
EmployeeId = e.EmployeeId,
Amount = e.QuantityByCicle,
Balance = e.QuantityByCicle
});
This query isn't running until it reaches to the ToList() code.
You should write it like this:
var y = (from e in db.FuelBenefits
where e.Active == true
select new FuelAmountEmployeeCycle
{
CycleId = 1,
EmployeeId = e.EmployeeId,
Amount = e.QuantityByCicle,
Balance = e.QuantityByCicle
}).ToList();
var y = db.FuelBenefits.Where(f => f.Active == true)
.Select(f => new FuelAmountEmployeeCycle
{
CycleId = 1,
EmployeeId = f.EmployeeId,
Amount = f.QuantityByCicle,
Balance = f.QuantityByCicle
})
.ToList();

Linq Filter By Sub List Object Property

I am trying to return all suppliers who's product categories are "Green" in the following list, I'm sure its simple but am struggling:
public class Supplier
{
public int SupplierID { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public Category(int ID, string Name)
{
this.ID = ID;
this.Name = Name;
}
}
public void FilterList()
{
//Get All Suppiers That Have Products In 'Green' Category
List<Supplier> GreenSupplierList = FullSupplierList.Where(x => x.Products.SelectMany(y => y.Category.Name == "Green")).ToList();
}
public List<Supplier> FullSupplierList
{
get
{
//Create List Object To Be Filter
List<Supplier> supplierList = new List<Supplier>();
int productCount = 0;
for (int i = 0; i < 10; i++)
{
Category category;
if (i > 3)
category = new Category(i, "Red");
else
category = new Category(i, "Green");
Supplier s = new Supplier();
s.SupplierID = 1;
s.Name = "Supplier " + i.ToString();
s.Products = new List<Product>();
for (int j = 0; j < 10; j++)
{
productCount += 1;
Product p = new Product();
p.ProductID = productCount;
p.Name = "Product " + productCount.ToString();
p.Category = category;
s.Products.Add(p);
}
supplierList.Add(s);
}
return supplierList;
}
}
FullSupplierList is just a simple method to return a populated list to work with or this example, but in the FilterList method is where I am trying to write the correct linq statement.
FullSupplierList.Where(s => s.Products.Any(p => p.Category.Name == "Green"))
.ToList();

How can i have a linq result set returned as a custom type

public class LearnerInfo
{
public string Id { get; set; }
public string Name { get; set; }
public LearnerInfo(string id, string name)
{
this.Id = id;
this.Name = name;
}
}
public class LearnerCourse
{
public string Id { get; set; }
public string ExpiredCount { get; set; }
public string Soonduecount { get; set; }
public string Currentmonthcount { get; set; }
public string Currentmonthplus1count { get; set; }
public string Currentmonthplus2count { get; set; }
public string Currentmonthplus3count { get; set; }
public string Currentmonthplus4count { get; set; }
public string Currentmonthplus5count { get; set; }
public string Subtotal { get; set; }
public LearnerCourse(string id, string exp, string soonDue, string current, string plus1, string plus2,
string plus3, string plus4, string plus5)
{
this.Id = id;
this.ExpiredCount = exp;
this.Soonduecount = soonDue;
this.Currentmonthcount = current;
this.Currentmonthplus1count = plus1;
this.Currentmonthplus2count = plus2;
this.Currentmonthplus3count = plus3;
this.Currentmonthplus4count = plus4;
this.Currentmonthplus5count = plus5;
}
public LearnerCourse()
{ }
}
public class InfoList : IEnumerable<CombinedInfo>
{
private List<CombinedInfo> _infoList = new List<CombinedInfo>();
public InfoList()
{
_infoList = new List<CombinedInfo>();
}
public void Add(CombinedInfo i)
{
_infoList.Add(i);
}
public IEnumerator<CombinedInfo> GetEnumerator()
{
return _infoList.GetEnumerator();
}
//IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public class CombinedInfo
{
public string Id { get; set; }
public string ExpiredCount { get; set; }
public string Soonduecount { get; set; }
public string Currentmonthcount { get; set; }
public string Currentmonthplus1count { get; set; }
public string Currentmonthplus2count { get; set; }
public string Currentmonthplus3count { get; set; }
public string Currentmonthplus4count { get; set; }
public string Currentmonthplus5count { get; set; }
public string Name { get; set; }
}
static void Main(string[] args)
{
LearnerCourse lc1 = new LearnerCourse("777", "1", "1", "0", "1", "0", "0", "0", "0");
LearnerCourse lc2 = new LearnerCourse("589", "1", "0", "0", "0", "0", "0", "0", "0");
LearnerInfo li1 = new LearnerInfo("777", "moe");
LearnerInfo li2 = new LearnerInfo("589", "larry");
LearnerCourse[] lCourses = new LearnerCourse[2];
lCourses[0] = lc1;
lCourses[1] = lc2;
LearnerInfo[] linfos = new LearnerInfo[2];
linfos[0] = li1;
linfos[1] = li2;
//test linq join for object array
var myJoin = (from c in lCourses
join i in linfos on c.Id equals i.Id
select new {
c.ExpiredCount,
c.Soonduecount,
c.Currentmonthcount,
c.Currentmonthplus1count,
c.Currentmonthplus2count,
c.Currentmonthplus3count,
c.Currentmonthplus4count,
c.Currentmonthplus5count,
c.Subtotal,
i.Id,
i.Name
});
foreach (CombinedInfo o in l)
{
//loop through and can add to list of type CombinedInfo
}
}
Instead of going through a foreach loop, i am having issues trying to get the result set from my linq query to just return a List.
suggestions?
Two things:
a) Project to a custom class, not an anonymous type. In your case you already have CombinedInfo defined - use it.
b) Use ToList() to force execution and convert the results to a List<T>
var list = (from c in lCourses
join i in linfos on c.Id equals i.Id
select new CombinedInfo() {
ExpiredCount = c.ExpiredCount,
Soonduecount = c.Soonduecount,
Currentmonthcount = c.Currentmonthcount,
Currentmonthplus1count = c.Currentmonthplus1count,
Currentmonthplus2count = c.Currentmonthplus2count,
Currentmonthplus3count = c.Currentmonthplus3count,
Currentmonthplus4count = c.Currentmonthplus4count,
Currentmonthplus5count = c.Currentmonthplus5count,
Subtotal = c.Subtotal,
Id = i.Id,
Name = i.Name
}).ToList();

LINQ and Entity Framework, get sum of related rows of a non mapped column

I want to get the sum of applicants that applied to a specific position, this should not be saved as a column.
My model is simple:
We have positions:
net developer
java developer
We have applicants:
Luis
John
etc
We have applicants per position
With this column or property I want to know how many people have applied to each position, depending on the status.
So in my mvc view I want to show something like:
Position Applied Accepted Rejected ... other status
.net developer 5 3 2
java developer 3 2 1
The real problem here is the linq query which I am not very expert.
EDIT: I think I needed to change where the linq query must be coded, I suppose it should be in the ApplicantPosition class instead of Position, I also changed the types of Position and Application to be ICollection.
Please see the modified code.
public class Position
{
public int id { get; set; }
[StringLength(20, MinimumLength=3)]
public string name { get; set; }
public int yearsExperienceRequired { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
[StringLength(20, MinimumLength = 3)]
public string name { get; set; }
public string telephone { get; set; }
public string skypeuser { get; set; }
public ApplicantImage photo { get; set; }
}
public class ApplicantPosition
{
public virtual ICollection<Position> appliedPositions { get; set; }
public virtual ICollection<Applicant> applicants { get; set; }
public DateTime appliedDate { get; set; }
public int StatusValue { get; set; }
public Status Status
{
get { return (Status)StatusValue; }
set { StatusValue = (int)value; }
}
[NotMapped]
public int numberOfApplicantsApplied
{
get
{
var query =
from ap in appliedPositions
select new
{
positionName = g.Key.name,
peopleApplied = g.Count(x => x.Status == Status.Applied),
};
return query.Count(); ---??
}
}
}
Use direct SQL with PIVOT operator. This is really not a case for Linq query.
You can paste this into LINQPad as C# Program and run.
public enum Status
{
Applied,
Accepted,
Rejected
}
public class Position
{
public int id { get; set; }
public string name { get; set; }
}
public class Applicant
{
public int ApplicantId { get; set; }
public string name { get; set; }
}
public class ApplicantPosition
{
public Position appliedPosition { get; set; }
public Applicant applicant { get; set; }
public DateTime appliedDate { get; set; }
public Status Status { get; set; }
}
void Main()
{
var p1 = new Position { id = 1, name = ".net developer" };
var p2 = new Position { id = 2, name = "java developer" };
var a1 = new Applicant { ApplicantId = 100, name = "Luis" };
var a2 = new Applicant { ApplicantId = 200, name = "John" };
var ap1 = new ApplicantPosition { appliedPosition = p1, applicant = a1, Status = Status.Applied };
var ap2 = new ApplicantPosition { appliedPosition = p1, applicant = a2, Status = Status.Accepted };
var ap3 = new ApplicantPosition { appliedPosition = p2, applicant = a2, Status = Status.Rejected };
var db = new[] { ap1, ap2, ap3};
var query =
from ap in db
group ap by ap.appliedPosition into g
select new
{
positionName = g.Key.name,
peopleApplied = g.Count(x => x.Status == Status.Applied),
peopleAccepted = g.Count(x => x.Status == Status.Accepted),
peopleRejected = g.Count(x => x.Status == Status.Rejected),
};
query.Dump();
}
The result will be:
positionName peopleApplied peopleAccepted peopleRejected
.net developer 1 1 0
java developer 0 0 1
According to my experiences you can use LinQ or Entity Framework just by mapping your tables in to a DBML to a Entity Framework Model file.
In other way Microsoft gives you a Dynamic LinQ class that you can use it.I think you map all your columns and user Dynamic LinQ class.Good luck

LINQ - Combine multiple lists to form a new list and align them by key?

I have two list of different columns, but each list have a common column with the same key, how do I combine them into a new list, i.e:
public class TradeBalanceBreak
{
public int CommID { get; set; }
public int CPFirmID { get; set; }
public double CreditDfferenceNotional { get; set; }
public string Currency { get; set; }
}
public class Commission
{
public int CommID { get; set; }
public PeriodStart { get; set; }
public ResearchCredit { get; set; }
}
public class CommissionList
{
public List<Commission> Commissions { get { return GetCommissions(); }}
private List<Commission> GetCommissions()
{
// retrieve data code ... ...
}
}
public class TradeBalanceBreakModel
{
public List<TradeBalanceBreak> TradeBalanceBreaks { get; set; }
}
public class CommissionModel
{
public List<CommissionList> CommissionLists { get; set; }
}
What I would like to achieve is to combine/flatten the TradeBalancesBreaks and CommissionLists (from the model classes) into one. The CommID is shared between the two.
Thanks.
Using Join (extension method version) -- after your update
var list1 = GetTradeBalanceBreaks();
var list2 = new CommisionsList().Commissions;
var combined = list1.Join( list2, l1 => l1.ID, l2 => l2.First().ID,
(l1,l2) = > new
{
l1.CommID,
l1.CPFirmID,
l1.CreditDifferenceNotional,
l1.Currency,
PeriodStarts= l2.SelectMany( l => l.PeriodStart ),
ResearchCredits = l2.SelectMany( l => l.ResearchCredit )
})
.ToList();
var combined = from p in PhoneNumbers
join a in Addresses on a.ID equals p.ID
select new {
ID = p.ID,
Name = p.Name,
Phone = p.Phone,
Address = a.Address,
Fax = a.Fax
};

Resources