I am trying to get Distinct results using EntityFramework and MS SQL query and filtering records on two conditions so I have used Distinct()
here is my code:
public List<ProductViewModel> GetPagedFilterProducts(int page, int type_id1, int type_id2)
{
int recordsPerPage = 20;
var skipRecords = page * recordsPerPage;
var results = _products.GetAll().Where(p => p.type1 == type_id1 && p.type2 == type_id2).Select(p => new ProductViewModel
{
productId = p.product_id,
productTitle = p.product_title,
}).OrderByDescending(p => p.productTitle).Skip(skipRecords).Take(recordsPerPage).ToList();
return results.Distinct().ToList();
}
I am using checkboxes to filter the records, so type_id can be more than two conditions (I mean there could be type_id3). How should I query so user can select more than two type conditions and it still gives me Distinct results.
How can I query on each condition separately and combine them together and get the Distinct results?
Please forgive me if I didn't explain my question properly.
public List<ProductViewModel> GetPagedFilterProducts(int page, int type_id1, int type_id2)
{
int recordsPerPage = 20;
var skipRecords = page * recordsPerPage;
var results = _products.GetAll().Where(p => p.type == type_id1 || p.type == type_id2).Select(p => new ProductViewModel
{
productId = p.product_id,
productTitle = p.product_title,
}).OrderByDescending(p => p.productTitle)
.Skip(skipRecords)
.Take(recordsPerPage).ToList();
return results.Distinct().ToList();
}
The basic idea behind the query,Value of checkbox for selecting items will compare in a table with a single column .
Related
This works fine. But how do I customize the conditions for adding filters?input is the value entered by the user
var resultList = dbContext.BuyerBill
.Include(x=>x.BuyerBillItems.Where(x=>x.Status == input.Status && x.BuildTime > input.BeginTime && x.BuildTime < input.EndTime))
.ToList();
the way I want:
var query = WhereIf(input.Status!=null,x=>x.Status == input.Status);
query = WhereIf(input.BeginTime!=null,x=>x.BuildTime > input.BeginTime);
query = WhereIf(input.EndTime!=null,x=>x.BuildTime > input.EndTime);
this is my entity
public class BuyerBill
{
public BuyerBill()
{
BuyerBillItems = new List<BuyerBillItems>();
}
public int Id {get;set;}
public int BuyUserId {get;set;}
public int OrderId {get;set;}
public List<BuyerBillItems> BuyerBillItems { get; set; }
....
}
public class BuyerBillItems
{
public int Id {get;set;}
public int BuyerBillId {get;set;}
public decimal Fee {get;set;}
public int Status {get;set;}
public dateTime CreateTime {get;set;}
public BuyerBill BuyerBill {get;set;}
....
}
1、If the user does not select the time query
Select * from BuyerBill as buy inner join BuyerBillItems As item On buy.Id=item.BuyerBillId
where item.Status=1
2、If the user selects the time query
Select * from BuyerBill as buy inner join BuyerBillItems as item on buy.Id=item.BuyerBillId
where item.Status=1 and item.BuildTime > '2022-7-19' and item.BuildTime < '2022-7-19'
How to use efcore to implement the SQL conditions I described?
Mainly, I want to filter sub entities according to conditions. If there is only one entity, I know Where() method can be used for filtering, but I don't know how to use conditional filtering for sub entities
I solved the above problem with LinqKit, but now I have a new problem。
var predicatess = PredicateBuilder.New<BuyerBillItems>(true);
predicatess = predicatess.And(x => x.CreateTime > StringHelper.AddDateTime("2022-07-16"));
predicatess = predicatess.And(x => x.Status == 2);
//I'm dumb and this line of code seems redundant. But I don't know how to convert implicitly
var convertPredicate = (Expression<Func<BuyerBillItems, bool>>)predicatess;
var query = dbContext.BuyerBill.AsExpandable().Include(x => x.BuyerBillItems.Where(x => convertPredicate.Invoke(x)))
.Where(x => x.BuyerBillItems.Any(s => convertPredicate.Invoke(s)))
.Where(x => x.BuyUserId == 4);
//If you don't use Select, everything is normal
var result1 = query.ToList();
//BuyerBillItemsDto result is incorrect after using Select
var result2 = query.Select(x => new BuyerBillDto
{
Id = x.Id,
BuyUserId = x.BuyUserId,
OrderId = x.OrderId,
BuyerBillItemsDto = mapper.Map<List<BuyerBillItems>, List<BuyerBillItemsDto>>(x.BuyerBillItems)
}).ToList();
I have to use select to filter the columns to avoid performance loss
Curently you cannot use own extensions in Include body. So, consider to write query in the following way:
var resultList = dbContext.BuyerBill
.Include(x => x.BuyerBillItems.Where(x =>
(input.Status == null || x.Status == input.Status && x.BuildTime > input.BeginTime && x.BuildTime < input.EndTime)) &&
(input.BeginTime == null || x.BuildTime > input.BeginTime) &&
(input.EndTime == null || x.BuildTime > input.EndTime)
)
.ToList();
Update based on updated reuiqrements
For fully custom projection, there is not needed to build Include, because Select specifies which data is needed to retrieve from database.
Query can be rewritten using WhereIf extension:
var result = dbContext.BuyerBill
.Select(b => new BuyerBillDto
{
Id = b.Id,
BuyUserId = b.BuyUserId,
OrderId = b.OrderId,
BuyerBillItemsDto = b.BuyerBillItems
.AsQueryable()
.WhereIf(input.Status != null, x => x.Status == input.Status)
.WhereIf(input.BeginTime != null, x => x.BuildTime > input.BeginTime)
.WhereIf(input.EndTime != null, x => x.BuildTime > input.EndTime)
.Select(item => new BuyerBillItemsDto
{
Id = item.Id,
// other properties
})
.ToList()
}).ToList();
Here is my code and i want to order by via date_added column. i tried all the possibilities but still the date_added column sorted via month instead of a year. Please guide where i need to put orderby statement.further the date_added return result in string datatype.
{
var records = (from r in db2.documents
select new
{
r.show_in_portal,
r.buyer_id,
r.advertiser_id,
r.contract_id,
r.campaign_id,
date_added = Dates.FormatDateToExt(r.date_added),
id = r.document_id,
name = r.filename,
location = r.filename,
r.publisher_id,
affiliate_id = (r.contract != null ? r.contract.publisher_id : -1),
document_type = r.document_type.type_name
});
if (campaign_id > 0)
records = records.Where(v => v.campaign_id == campaign_id);
//if (creativeid > 0)
// records = records.Where(v => v.id == creativeid);
if (affid > 0)
records = records.Where(v => v.publisher_id == affid);
if (contid > 0)
records = records.Where(v => v.contract_id == contid);
if (advertiserid > 0)
records = records.Where(v => v.advertiser_id == advertiserid);
if (buyerid > 0)
records = records.Where(v => v.buyer_id == buyerid);
GridOut(context, records.ToArray());
}
public static string FormatDateToExt(DateTime? input)
{
return FormatDateToExt(input, 0);
}
public static string FormatDateToExt(DateTime? input, int time_offset = 0)
{
return input != null ? input.Value.AddHours(-1 * time_offset).ToString("MM/dd/yyy h:mm:ss tt") : "";
}
The result of your query is a sequence of some anonymous type. Date_Added is one of the properties of this anonymous type, so after you created your query you can order by Date_Added.
The type of Date_Added is the returned type of Dates.FormatDateToExt(...). Alas you forgot to inform us about this type. Is it a DateTime? Is it a string? If you order by this type do you get the sorting order that you want?
If so, just add the OrderBy at the end:
var records = db2.documents.Select(document => new
{
Id = document.document_id,
Portal = document.Show,
BuyerId = document.buyer_id,
AdvertiserId = document.advertiser_id,
...
DateAdded = Dates.FormatDateToExt(document.date_added),
});
if (campaign_id > 0)
records = records.Where(record => record.campaign_id == campaign_id);
if (affid > 0)
records = records.Where(record => record.publisher_id == affid);
... // etc. other ifs and other wheres
records = records.OrderBy(record => record.DateAdded);
It is a good thing to do the Sorting at the end, because this means that you will have to sort fewer records. All records that don't pass all Wheres, won't have to be sorted.
Finally a small hint: did you see, that if you use proper identifiers, that your queries will be easier to read? It is good practice to use plural nouns for collections of items, and singular nouns for elements of the collection:
var novels = dbContext.Books.Where(book => book.Type == BookType.Novel)
Consider making your dates uniform by using ISO 8601 ones (convert them on the fly in your Linq query), as they're made to be sortable.
You can put the orderby clause after the from.
Read:
https://www.c-sharpcorner.com/UploadFile/mahesh/working-with-datetime-using-C-Sharp/
https://dev.to/adnauseum/sorting-iso-8601-timestamps-5am2
Im trying to create a table that counts all orders and groups them in a table from sql to linq to use in a bar graph with google charts.
Table`
Orders Status
8 Created
3 Delayed
4 Enroute
sql
SELECT Count (OrderID) as 'Orders', order_status FROM [ORDER]
where order_status ='Created'OR order_status= 'Delayed' OR order_status='Enroute'
group by order_status
controller
public ActionResult GetChart()
{
var Orders = db.Order.Select(a => new { a.OrderID, a.order_status })
.GroupBy(a => a.order_status);
return Json(Orders, JsonRequestBehavior.AllowGet);
}
this is not displaying the correct results as the linq seems to be wrong.
can someone please point me in the right direction? I am relatively new to this.
thanks in advance.
This should work:-
var result = db.Order.Where(x => x.order_status == "Created"
|| x.order_status == "Delayed"
|| x.order_status == "Enroute")
.GroupBy(x => x.order_status)
.Select(x => new
{
order_status = x.Key,
Orders = x.Count()
});
Or if you prefer query syntax then:-
var result = from o in db.Order
where o.order_status == "Created" || o.order_status == "Delayed"
|| o.order_status == "Enroute"
group o by o.order_status
select new
{
orderStatus = x.Key,
Counts = x.Count()
};
I think you want to group by Status and count total number of orders in each group (I build a simple console program to demonstrate). I suppose the data is:
Orders Status
8 Created
3 Delayed
4 Enroute
2 Created
1 Delayed
Order.cs
public class Order
{
public Order(int orderId, string status)
{
OrderId = orderId;
Status = status;
}
public int OrderId { get; set; }
public string Status { get; set; }
}
Program.cs
class Program
{
static void Main(string[] args)
{
// Data
var orders = new List<Order>
{
new Order(8, "Created"),
new Order(3, "Delayed"),
new Order(4, "Enroute"),
new Order(2, "Created"),
new Order(1, "Delayed"),
};
// Query
var query = orders
.GroupBy(x => x.Status)
.Select(x => new {Status = x.Key, Total = x.Count()});
// Display
foreach (var item in query)
{
Console.WriteLine(item.Status + ": " + item.Total);
}
Console.ReadLine();
}
}
The one you need to focus in is query. After using GroupBy, you will have a list of groups. For each group, the Key is the criteria to group (here is the Status). Then, we call Count() to get the total number of element in that group.
So, from the program above, the output should be:
Created: 2
Delayed: 2
Enroute: 1
Have the following code: a entity linq query results in a select of two tables.
All data is available in the query, but can't get the result to split into two lists.
public Tuple<List<sale>, List<product>> SearchProduct(int productId = -1, string searchProduct = "")
{
//ToDo: searchProduct not working...gives nothing
var companyId = DalSession.DalGetCurrentUser().Company_ID;
using (var entity = new secondsoftEntities())
{
var query = (from s in entity.sales
join p in entity.products on s.Product_ID equals p.ProductID
where productId > 0 ? s.Product_ID == productId : s.Company_ID == companyId
select new { s, p }).ToList();
if (!string.IsNullOrEmpty(searchProduct))
{
query = query.FindAll(x => x.p.Description.ToLower().Contains(searchProduct.ToLower()));
}
// split s as List<sale> and p as List<product> to tuple output
return Tuple.Create(new List<sale>(), new List<product>() );
}
}
In the query result I see s and p, but how the exact them as a list with there properties in it so I can return them in Tuple.
Thanx Dinand
return Tuple.Create(query.Select(x => x.s).ToList(),
query.Select(x => x.p).ToList());
I have two tables:
Invoices (InvoiceID, InvoiceNumber)
Invoices_Products (InvoiceID, ProductID, IsFinalized)
I show a list of all invoices, and there are buttons to filter by "finalized" or "not finalized" invoices. A finalized invoice is one where every product on it is IsFinalized==true.
At the moment I have the following code which is performing quite slowly:
IEnumerable<Invoice> invoices = db.Invoices;
if (isFinalized) // filter by finalized invoices
{
List<Invoice> unfinalizedInvoices = new List<Invoice>();
foreach (var invoice in invoices)
{
int invoicesProductsCountTotal = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID).Count();
int invoicesProductsCountFinalized = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID && l.IsFinalized == true).Count();
if (invoicesProductsCountTotal != invoicesProductsCountFinalized)
{
unfinalizedInvoices.Add(invoice);
}
}
invoices = invoices.Except(unfinalizedInvoices);
}
else
{
List<Invoice> finalizedInvoices = new List<Invoice>();
foreach (var invoice in invoices)
{
int invoicesProductsCountTotal = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID).Count();
int invoicesProductsCountFinalized = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID && l.IsFinalized == true).Count();
if (invoicesProductsCountTotal == invoicesProductsCountFinalized && invoicesProductsCountFinalized > 0)
{
finalizedInvoices.Add(invoice);
}
}
invoices = invoices.Except(finalizedInvoices);
}
I realize this isn't optimal but I like spreading out my LINQ so that I can read and understand it.My question: Is there any way I could make this query faster using .All or .Any or something, or do I need to rethink my database design (possibly adding an extra column to the Invoices table)
edit: Third table is Products (ProductID, ProductNumber) but you knew that
At the moment you're loading all your invoices and then loading the products for each invoice. This is bound to be slow (and it will become a lot slower when you start adding a lot of invoice).
You should create a many-to-many relationship in EntityFramework. (see example)
Your classes would look like this:
class Invoice
{
List<Product> Products {get; set;}
}
class Product
{
bool IsFinalized {get; set;}
}
Now you can use LINQ to make sure that only SQL statement is executed which fetches only the data you want:
var invoices = db.Invoices.Where(i => i.Products.All(p => p.IsFinalized == finalized));
Iterating over each Invoice and then make additional requests to the database will be very slow. Let your query get all the informations at once and iterate through the results instead.
var result = from invoice in db.Invoices
join invoicedProduct in db.Invoices_Products
on invoice.InvoiceId equals invoicedProduct.InvoiceId
select new
{
InvoiceId = invoice.InvoiceId,
ProductId = invoicedProduct.ProductId,
IsFinalized = invoicedProuct.IsFinalized
};
var grpResult = from record in result
group record by record.ProductId into productGrp
select productGrp;
foreach( var grp in grpResult )
{
Console.WriteLine( "ProductId: " + grp.Key.ToString( ) );
Console.WriteLine( "TotalCount: " + grp.Count( ).ToString( ) );
Console.WriteLine( "Finalized: " + grp.Where( item => item.IsFinalized ).Count( ).ToString( ) );
}
if (isFinalized)
{
invoices = invoices.Where(l => l.Invoices_Products.All(m => m.IsFinalized == true));
}
else
{
List<Invoice> finalizedInvoices = invoices.Where(l => l.Invoices_Products.All(m => m.IsFinalized == true)).ToList();
invoices = invoices.Except(finalizedInvoices);
}
^^ this seems to have improved performance dramatically. oh well, thanks for listening