I have a linq query that executes successfully, one of the columns returned is a decimal type that is used to represent prices in pounds and pence (there will never be any negative values)
I want to be able to strip out the pounds and pence into separate Properties of my projection, however when using functionality such as
var result= from j in context.Products
select
new{
Price = t.Price,
PricePounds = Math.Truncate(t.Price)
};
I get an error that Math.truncate is not supported as it cannot be translated into a store expression. How can I get the pounds value from this query?
If you don't need to do anything else in the database after that, the simplest approach is just to perform the truncation client-side:
var query = context.Products
.AsEnumerable() // Everything from here is LINQ to Objects
.Select(p => new {
p.Price,
PricePounds = Math.Truncate(p.Price)
});
Note that you might also want to just cast to int - and that might be supported in EF already.
EDIT: As noted in comments, you may want to perform a projection first, e.g.
var query = context.Products
.Select(p => new { p.Price, p.SomethingElse })
.AsEnumerable() // Everything from here is LINQ to Objects
.Select(p => new {
p.Price,
PricePounds = Math.Truncate(p.Price),
p.SomethingElse
});
(Where SomethingElse is another property you're interested in, as an example - I doubt that you only want the price.)
This will avoid the entire entity being fetched when you only want a few properties.
You may try:
var result= from j in context.Products
select
new {
Price = t.Price,
PricePounds = EntityFunctions.Truncate(t.Price, 0)
};
The case is Math.Truncate cannot be translated into SQL where EntityFunctions.Truncate should be.
Related
I'm trying to use LINQPad to see outputs of my queries I'm writing for a C# WinForms application. I'm more just confused on how to interpret the outcome on a Dump() method and what is happening behind the scenes.My intended outcome is to have one SampleNumber per group of SampleData. SampleData is coming out of the database as a string split by commas.
Also, please spare me; I know how I name stuff is horrible and I'm planning to adjust once I get the data coming out correctly!
Here is my initial query:
var thinthroughoutQuery = (from a in ThicknessBlobs
join b in SummaryDataItems on a.SampleNumber equals b.SampleNumber
where (a.Source == "Thickness[0,0]")
&& b.TopBottomStatusFK == 3
orderby a.PKId descending
select a).Take(1000).ToList();
Here is where I attempt to take the string of SampleData, cut it at the delimiter, group by SampleNumber, and parse it as an integer:
var intThinThroughoutQuery = thinthroughoutQuery.Select(row => new { SampleNumber = row.SampleNumber, Value = row.SampleData.Split(",").Select(int.Parse) })
.SelectMany(group => group.Value.Select(value => new { group.SampleNumber, SampleData = value })).ToArray();
Here is the output from using Dump() in LINQPad:
To me, this appears like there are not distinct SampleNumbers and that each piece of SampleData separately maps to a repeating SampleNumber.
For further clarification, I want to be able to access the data and have it be like this:
Rather than:
You are missing GroupBy/ToLookup after flattening with SelectMany:
var intThinThroughoutQuery = thinthroughoutQuery.Select(row => new { SampleNumber = row.SampleNumber, Value = row.SampleData.Split(",").Select(int.Parse) })
.SelectMany(group => group.Value.Select(value => new { group.SampleNumber, SampleData = value }))
.ToLookup(row => row.SampleNumber, row => row.SampleData);
I have a query that is similar to the following (my actual query has three sections like this and then Concats them together and applies some additional filters and sorting).
var articles = from p in Repository.Query<Product>()
let article = p.Article
let office = p.TariffCategory.Office
where p.IsDeleted == false
select new
{
OfficeId = office.Id,
Office = office.Name,
Category = p.TariffCategory.Description,
ArticleId = article.Id,
Article = article.Title,
Destinations = p.ProductDestinations.Select(d => new { Id = d.DestinationId, Name = d.Destination.Description }),
GlobalDestinations = p.AllDestinationsInOffice,
article.LastReviewedDate,
article.CreatedDate,
article.CreatedByEmployee
};
Everythings seems right except my assignment to Destinations. That line produces the following error.
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
If I remove that line, everything works as expected. Is there any way to perform a query like this?
I had a bit of a think, and rather than doing a join as I suggested, it may make sense to start the query at ProductDestination. What we're interested in is a row for each product+destination combination, much like you'd see via regular SQL queries. Once we've got that, we can apply grouping to the result so that we're closer to the representation you had
var data = Repository.Query<ProductDestination>()
.Where(pd => !pd.Product.IsDeleted)
.Select(pd =>
new {
Product = pd.Product,
Destination = pd.Destination,
})
.GroupBy(pd => pd.Product)
//I'm not in a position to test if EF will successfully run the below, so .ToList()
//May not be required. However, the bulk of the work is done in the database, anyway.
//.ToList()
.Select(g => new {
OfficeId = g.Key.TariffCategory.Office.Id,
Office = g.Key.TariffCategory.Office.Name,
Category = g.Key.TariffCategory.Description,
ArticleId = g.Key.Article.Id,
Article = g.Key.Article.Title,
Destinations = g.Select(gg => new { Id = gg.Destination.DestinationId, Name = gg.Destination.Description }),
GlobalDestinations = g.Key.AllDestinationsInOffice,
g.Key.Article.LastReviewedDate,
g.Key.Article.CreatedDate,
g.Key.Article.CreatedByEmployee
});
I'm pretty sure the above should work without the ToList(), but I'm not confident to say it 100% will work. However, as noted, the bulk of the work is done in the database, the final projection shouldn't be too intensive, even if it's done in memory. However, should the ToList() be required, we would need to modify the GroupBy to return all fields we select via Key, otherwise we're going to have issues with lazy loading and N+1 queries.
I have a database that contains 3 tables:
Phones
PhoneListings
PhoneConditions
PhoneListings has a FK from the Phones table(PhoneID), and a FK from the Phone Conditions table(conditionID)
I am working on a function that adds a Phone Listing to the user's cart, and returns all of the necessary information for the user. The phone make and model are contained in the PHONES table, and the details about the Condition are contained in the PhoneConditions table.
Currently I am using 3 queries to obtain all the neccesary information. Is there a way to combine all of this into one query?
public ActionResult phoneAdd(int listingID, int qty)
{
ShoppingBasket myBasket = new ShoppingBasket();
string BasketID = myBasket.GetBasketID(this.HttpContext);
var PhoneListingQuery = (from x in myDB.phoneListings
where x.phonelistingID == listingID
select x).Single();
var PhoneCondition = myDB.phoneConditions
.Where(x => x.conditionID == PhoneListingQuery.phonelistingID).Single();
var PhoneDataQuery = (from ph in myDB.Phones
where ph.PhoneID == PhoneListingQuery.phonePageID
select ph).SingleOrDefault();
}
You could project the result into an anonymous class, or a Tuple, or even a custom shaped entity in a single line, however the overall database performance might not be any better:
var phoneObjects = myDB.phoneListings
.Where(pl => pl.phonelistingID == listingID)
.Select(pl => new
{
PhoneListingQuery = pl,
PhoneCondition = myDB.phoneConditions
.Single(pc => pc.conditionID == pl.phonelistingID),
PhoneDataQuery = myDB.Phones
.SingleOrDefault(ph => ph.PhoneID == pl.phonePageID)
})
.Single();
// Access phoneObjects.PhoneListingQuery / PhoneCondition / PhoneDataQuery as needed
There are also slightly more compact overloads of the LINQ Single and SingleOrDefault extensions which take a predicate as a parameter, which will help reduce the code slightly.
Edit
As an alternative to multiple retrievals from the ORM DbContext, or doing explicit manual Joins, if you set up navigation relationships between entities in your model via the navigable join keys (usually the Foreign Keys in the underlying tables), you can specify the depth of fetch with an eager load, using Include:
var phoneListingWithAssociations = myDB.phoneListings
.Include(pl => pl.PhoneConditions)
.Include(pl => pl.Phones)
.Single(pl => pl.phonelistingID == listingID);
Which will return the entity graph in phoneListingWithAssociations
(Assuming foreign keys PhoneListing.phonePageID => Phones.phoneId and
PhoneCondition.conditionID => PhoneListing.phonelistingID)
You should be able to pull it all in one query with join, I think.
But as pointed out you might not achieve alot of speed from this, as you are just picking the first match and then moving on, not really doing any inner comparisons.
If you know there exist atleast one data point in each table then you might aswell pull all at the same time. if not then waiting with the "sub queries" is nice as done by StuartLC.
var Phone = (from a in myDB.phoneListings
join b in myDB.phoneConditions on a.phonelistingID equals b.conditionID
join c in ph in myDB.Phones on a.phonePageID equals c.PhoneID
where
a.phonelistingID == listingID
select new {
Listing = a,
Condition = b,
Data = c
}).FirstOrDefault();
FirstOrDefault because single throws error if there exists more than one element.
In EF, if I have a list of primatives (List), "joining" that against a table is easy:
var ids = int[]{1,4,6}; //some random values
var rows = context.SomeTable.Where(r => ids.Contains(r.id))
This gets much more complicated the instant you want to join on multiple columns:
var keys = something.Select(s => new { s.Field1, s.Field2 })
var rows = context.SomeTable.Where(r => keys.Contains(r => new { s.Field1, s.Field2 })); // this won't work
I've found two ways to join it, but neither is great:
Suck in the entire table, and filtering it based on the other data. (this gets slow if the table is really large)
For each key, query the table (this gets slow if you have a decent number of rows to pull in)
Sometimes, the compromise I've been able to make is a modified #1: pulling in subset of the table based on a fairly unique key
var keys = something.Select(s => s.Field1)
var rows = context.SomeTable.Where(r => keys.Contains(s.Field1)).ToList();
foreach (var sRow in something)
{
var joinResult = rows.Where(r => r.Field1 == sRow.Field1 && r.Field2 == sRow.Field2);
//do stuff
}
But even this could pull back too much data.
I know there are ways to coax table valued parameters into ADO.Net, and ways I can build a series of .Where() clauses that are OR'd together. Does anyone have any magic bullets?
Instead of a .Contains(), how about you use an inner join and "filter" that way:
from s in context.SomeTable
join k in keys on new {k.Field1, k.Field2} equals new {s.Field1, s.Field2}
There may be a typo in the above, but you get the idea...
I got exactly the same problem, and the solutions I came up with were:
Naive: do a separate query for each local record
Smarter: Create 2 lists of unique Filed1 values and unique Fiels2 values, query using 2 contains expressions and then you will have to double filter result as they might be not that accurate.
Looks like this:
var unique1 = something.Select(x => x.Field1).Distinct().ToList();
var unique2 = something.Select(x => x.Field2).Distinct().ToList();
var priceData = rows.Where(x => unique1.Contains(x.Field1) && unique2.Contains(x.Field2));
Next one is my own solution which I called BulkSelect, the idea behind it is like this:
Create temp table using direct SQL command
Upload data for SELECT command to that temp table
Intercept and modify SQL which was generated by EF.
I did it for Postgres, but this may be ported to MSSQL is needed. This nicely described here and the source code is here
You can try flattening your keys and then using the same Contains pattern. This will probably not perform great on large queries, although you could use function indexes to store the flattened key in the database...
I have table Test with columns K1 int, K2 int, Name varchar(50)
var l = new List<Tuple<int, int>>();
l.Add(new Tuple<int, int>(1, 1));
l.Add(new Tuple<int, int>(1, 2));
var s = l.Select(k => k.Item1.ToString() + "," + k.Item2.ToString());
var q = Tests.Where(t => s.Contains(t.K1.ToString() + "," + t.K2.ToString()));
foreach (var y in q) {
Console.WriteLine(y.Name);
}
I've tested this in LinqPad with Linq to SQL
First attempt that didn't work:
I think the way to write it as a single query is something like this
var keys = something.Select(s => new { s.Field1, s.Field2 })
var rows = context.SomeTable.Where(r => keys.Any(k => r.Field1 == k.Field1 && r.Field2 == k.Field2));
Unfortunately I don't have EF on this laptop and can't even test if this is syntactically correct.
I've also no idea how performant it is if it works at all...
var rows =
from key in keys
join thingy in context.SomeTable
on 1 = 1
where thingy.Field1 == key && thingy.Field2 == key
select thingy
should work, and generate reasonable SQL
I have an Entity Framework entity Provider, with a list of rating votes for that provider. My current queries look something like this:
int previousVote = provider.ProviderRankings.FirstOrDefault(r => r.UserId == CurrUserId);
double averageVote = provider.ProviderRankings.Average(r => r.Rating);
int totalVotes = provider.ProviderRankings.Count();
This seems functionally correct. However, I believe this will result in three additional trips to the database. Is there anyway to have these requests combined into a single query such that only one SQL query will be sent, and all results can be returned with only one additional trip to the server?
You could combine the two aggregates fairly easily using a Group By:
Multiple SQL aggregate functions in a single Linq-to-Entities query
I am pretty sure the FirstOrDefault will work if you choose a suitably vague key for the grouping (for example key = 0) i.e:
from t in ProviderRankings
group t by key = 0
into g
select new {
previousVote = g.FirstOrDefault(r => r.UserId == CurrUserId),
totalVotes = g.Count(),
averageVote = g.Average(x => x.Rating)
}