Using a calculated value in the OrderBy clause with EF - c#

I'm trying to use a calculated value in my OrderBy clause in a LINQ query.
The error I am getting is:
DbArithmeticExpression arguments must have a numeric common type.
My model looks like this:
public class PostModel
{
public int ID { get; set; }
public DateTime Created { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string FilePath { get; set; }
public float Rank { get; set; }
public UserProfile Creator { get; set; }
public bool ShowPost { get; set; }
public PostModel()
{
Created = DateTime.Now;
Rank = 0;
ShowPost = false;
}
}
and I'm trying to select posts using this:
var todaysDate = DateTime.Now.AddDays(-10);
var result = _database.Posts
.Where(p => p.ShowPost == true)
.OrderBy(x => ((float)x.Rank) - (((float)(x.Created - todaysDate).TotalDays)) / 2f)
.Skip(page * StaticVariables.ResponseDataPageSize)
.Take(StaticVariables.ResponseDataPageSize)
.Select(s => new
{
id = s.ID,
rank = s.Rank,
title = s.Title,
description = s.Description
}
);
It's the order by causing the error. I first thought it was that I was not casting all my variables to the same type, but adding (float) does not seem to help.
The purpose of the code is to make make high ranking posts fall down the list over time as to allow newer information to be shown.
Any ideas?

Use EntityFunctions in LinqToEntity:
EntityFunctions.DiffDays(todaysDate, x.Created)

Related

I am unable to convert StringBuilder to string in c# asp.net MVC

My invoice class has a StringBuilder property so that if adjustments are made, the user adds notes as the details of why the adjustment was made. This property is set to a new empty StringBuilder upon creation. Only in edit mode will it get .Append() added to. From what I understand, this is more efficient than using adjustmentNotes += newAdjustmentNotes every time new notes are added.
My Invoice.cs class is as follows.
public class Invoice
{
[Key]
public int InvoiceID { get; set; }
[ForeignKey(nameof(Customer))]
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<Job> Jobs { get; set; }
public double InvoiceAmount { get; set; }
public StringBuilder AdjustmentNotes { get; set; }
public bool Paid { get; set; }
}
When populating my list or details view, I query the database as follows:
using (var ctx = new ApplicationDbContext())
{
var query = ctx.Invoices
.Where(e => e.InvoiceID >= 0)
.Select(e => new InvoiceListItem
{
InvoiceID = e.InvoiceID,
CustomerID = e.CustomerID,
InvoiceAmount = e.InvoiceAmount,
Paid = e.Paid,
AdjustmentNotes = e.AdjustmentNotes.ToString()
});
return query.ToArray();
}
Then I get the following error message.
System.NotSupportedException: 'Values of type 'StringBuilder' can not be converted to string.'
But according to Microsoft documentation, other questions on this forum, and various resources this is the proper way to accomplish this.
I can certainly change it back to a string and use += as needed. There will not be too many notes for this particular property, but can someone please explain what I am doing wrong? Thank you so much!
EDIT: For clarification, here is my InvoiceListItem model class:
public class InvoiceListItem
{
public int InvoiceID { get; set; }
public int CustomerID { get; set; }
public List<int> JobIDs { get; set; }
public double InvoiceAmount { get; set; }
[UIHint("Bool")]
public bool Paid { get; set; }
public string AdjustmentNotes { get; set; }
}
EF Core says in the error message :
I don't know how convert StringBuilder.ToString() to target database language.
The solution is to do the conversion after the query :
var result = ctx.Invoices
.Where(e => e.InvoiceID >= 0)
.ToList() // First execute the query
.Select( // Second convert Invoice to InvoiceListItem
e => new InvoiceListItem {
InvoiceID = e.InvoiceID,
CustomerID = e.CustomerID,
InvoiceAmount = e.InvoiceAmount,
Paid = e.Paid,
AdjustmentNotes = e.AdjustmentNotes.ToString()
}
);
If you want limit the returned properties in the query, you can use intermediate type like :
var query = ctx.Invoices
.Where(e => e.InvoiceID >= 0)
.Select(
e => new {
e.InvoiceID,
e.CustomerID,
e.InvoiceAmount,
e.Paid,
AdjustmentNotes
}
);
var result = query.ToList().
.Select(
e => new InvoiceListItem {
InvoiceID = e.InvoiceID,
CustomerID = e.CustomerID,
InvoiceAmount = e.InvoiceAmount,
Paid = e.Paid,
AdjustmentNotes = e.AdjustmentNotes.ToString()
}
);
PS :
I found no information about EF Core support StringBuilder type, but
your context's model is build with success then EF Core seems to support StringBuilder type...
If there is support, it seems very limited (see the error in your question). Maybe you can consider use String instead of StringBuilder in entity class.

How to get last Id that contain HardwarId

I got an issue to reclaim Hardware that has exact ID (e.g. ID=5). There is my code:
class HardwareTransfer{
public int Id { set; get; }
public ICollection<Hardware> Hardwares { get; set; }
}
class Hardware{
public int Id { set; get; }
public string Title { set; get; }
}
How to get last HardwareTransfer.Id of HardwareTransfer, that contains Hardwares.Id = 5?
you can use this code
//_listHardwareTransfer is a List Of HardwareTransfer
var maxId=_listHardwareTransfer.Where(x => x.Hardwars.Contains(5)).Max(x => x.Id);
There are several ways how you can obtain this. By using LINQ (preferred way):
myHardwareTransfer.Hardwares.Last(a => a.Id == 5);
In C#:
Hardware lastFound;
foreach(var nHardware in myHardwareTransfer.Hardwares)
if(nHardware.Id == 5)
lastFound = nHardware;

Linq Value cannot be null on FK

Using C# MVC5 Visual studio 2015.
I have a method that contains the following code:
public List<OffersOnPropertyViewModel> Build(string buyerId)
{
var filtered = _context.Properties.Where(x => x.Offers.Any(c => c.BuyerUserId == buyerId)).ToList();
var model = filtered.Select(c =>
{
var item = new OffersOnPropertyViewModel()
{
PropertyType = c.PropertyType,
NumberOfBedrooms = c.NumberOfBedrooms,
StreetName = c.StreetName,
Offers = c.Offers.Where(d => d.BuyerUserId == buyerId).Select(x => new OfferViewModel
{
Id = x.Id,
Amount = x.Amount,
CreatedAt = x.CreatedAt,
IsPending = x.Status == OfferStatus.Pending,
Status = x.Status.ToString(),
BuyerUserId = x.BuyerUserId
}),
};
return item;
}).ToList();
//TODO: refactor, shorten linq, duping where clause
return model;
}
Here is the model:
public class Property
{
[Key]
public int Id { get; set; }
[Required]
public string PropertyType { get; set; }
[Required]
public string StreetName { get; set; }
[Required]
public string Description { get; set; }
[Required]
public int NumberOfBedrooms { get; set; }
[Required]
public string SellerUserId { get; set; }
public bool IsListedForSale { get; set; }
public ICollection<Offer> Offers { get; set; }
}
In the DB Offers table has the property id as its FK.
The method fails at runtime saying the Value cannot be null.
When I step through I notice the filtered results (in the example its 1 result), is saying offers is null. Although the query just filtered the results based on "x.Offers".
I simply need a way to retrieve a list of property's that have offers made by the buyerId provided. Is my approach wrong? or am i missing a one liner?
Thanks
You will need to add Include() to your LINQ query to bring in child objects, as follows:
var filtered = _context.Properties.Include("Offers")
.Where(x => x.Offers.Any(c => c.BuyerUserId == buyerId)).ToList();
The reason your filter works with the Any() is because when generating the SQL query, this part forms the WHERE clause and is not included in the SELECT.

Add data to a list insde a list where item in the prerent list

I would like to select a where statement that adds items to a list where only product codes match. I have it so it gets all of the products sold in the sale but I would like there were statement to get only products in this sale.
PS: This is really hard to explain
Model
public class userSales
{
public string Name { get; set; }
public string UserName { get; set; }
public int Sale_Id { get; set; }
public int CostumerID { get; set; }
public string Sale_Date { get; set; }
public string Paid { get; set; }
public Nullable<int> Sale_Cost { get; set; }
public string Discount_Code { get; set; }
public List<SaleProduct> saleProductsList { get; set; }
}
public class SaleProduct
{
public int SaleID { get; set; }
public string ProductCode { get; set; }
public int ProductCount { get; set; }
public string Image_Path { get; set; }
public string Shoot_Date { get; set; }
public string Shoot_Info { get; set; }
}
Linq statement where I'm having trouble:
var test = (from _ClientData in db.ClientDatas
join _salesInfo in db.Sales_Infoes
on _ClientData.CostumerID
equals _salesInfo.CostumerID
where _ClientData.UserName == _userName
select new userSales()
{
CostumerID = _ClientData.CostumerID,
Name = _ClientData.Name,
UserName = _ClientData.UserName,
Sale_Id = _salesInfo.Sale_Id, // This is the item i would like to use in my were statement
Sale_Date = _salesInfo.Sale_Date,
Sale_Cost = _salesInfo.Sale_Cost,
Discount_Code = _salesInfo.Discount_Code,
Paid = _salesInfo.Paid,
// Problem here
saleProductsList = db.SaleProducts.Where()
}).ToList();
Got to this based on the answer:
var reult = db.ClientDatas.Where(a => a.UserName == _userName)
.Join(db.Sales_Infoes,
a => a.CostumerID,
b => b.CostumerID,
(a, b) => new userSales()
{
CostumerID = a.CostumerID,
Discount_Code = b.Discount_Code,
Sale_Cost = b.Sale_Cost,
Sale_Id= b.Sale_Id,
Name = a.Name,
Sale_Date = b.Sale_Date,
UserName = a.UserName,
Paid = b.Paid,
saleProductsList = db.SaleProducts.Where(c => c.SaleID == b.Sale_Id).ToList()
}).ToList();
You're not looking for a where, you're looking for a join. Where filters the results on a single table, join intersects two tables which is actually what you want here.
var result = db.Sales_Infoes.Where(x => x.UserName == _userName)
.Join(db.ClientDatas,
x => x.Sale_Id,
y => y.Sale_id,
(x, y) => new userSales() {
// x is SalesInfo obj y is ClientDatas obj do assignement here
Name = y.Name,
Sale_Date = y.Sale_date
}).ToList();
Just fyi I haven't had a chance to test that but it's the basic idea. You don't need a select like in your statement because the last argument I'm passing into join is the lambda (x, y) => ... in that case x and y are the current row from each table (that we've gotten from applying our where to the user sales table then joining those results into the salesproduct table) so whatever projections you want to do occur there. The other two method args above that are the telling join which fields to compare, it's the 'key selector' lambda expression for each table.

how to map result to right properties object using linq

I have problem counting unique value in data base I have user table with status the status are Unknown = 0,In = 1,Out = 2,UnknownWasIn = 3,UnknownWasOut = 4, the problem are I don't have lookup table for this value in database becouse of that we have hard coded it. I have create class StatusCounter
using System;
using System.Dynamic;
public class StatusCounter
{
public int Total { get; set; }
public int In { get; set; }
public int Out { get;set ; }
public int Unknow { get; set; }
public DateTime LastUpdatedDateTime { get; set; }
}
just want to ask how can I map value to the right properties using linq
var result = from people in TBL_People
where people.Deleted == false
group people by people.Status into s
select new StatusCounter
TBLPeople
public partial class TBLPeople
{
public int PersonID { get; set; }
public Nullable<int> Status { get; set; }
public string Email { get; set; }
}
You need to do one big group so you can do the individual counts. Not sure if there is a more elegant way to do that other than just grouping by a constant.
var result =
from people in TBL_People
where people.Deleted == false
group people by 1 into s
select new StatusCounter
{
Total = s.Count(),
In = s.Count(p => p.Status == 1),
Out = s.Count(p => p.Status == 2),
Unknown = s.Count(p => p.Status == 3 || p.Status == 4),
LastUpdatedDateTime = DateTime.Now
}
I'm not sure what you want to set LastUpdatedDateTime to since the people table doesn't have a date column.

Categories

Resources