I have a UserVote table that is based on the following domain object:
public class UserVote {
public Guid Id {get;set;}
public Guid PostId {get;set;}
public Guid UserId {get;set;}
public VoteType VoteType {get;set;}
}
public enum VoteType {
Upvote = 1,
Downvote = 0
}
How can I write a query to get the total count of VoteType for unique PostId's?
I've tried grouping by PostId and then calling Count but it didn't work:
_ctx.UserVotes.Where(x=>x.PostId == PostId).GroupBy(x=>x.PostId).Count()
I actually figured it out. I needed to cast the enum to an int
_ctx.Where(x => x.PostId == PostId).Sum(x => (int)x.VoteType);
Try this.
_ctx.UserVotes.Count(x=> x.PostId == PostId && x.VoteType == VoteType.Up);
Related
My model looks like this
public partial class EditModel
{
public int Id { get; set; }
...
public string Item { get; set; }
}
My SearchItems method header looks like this
protected async Task<IEnumerable<ListItem>> SearchItems(string value)
which returns 'list' of these
public partial class ListItem
{
public string Id { get; set; }
public string Name { get; set; }
}
How do I get my MudAutocomplete to show the Name, yet return/bind the Id?
<MudAutocomplete T="ListItem" Label="Select item" #bind-Value="EditModel.Item"
Clearable="true"
MinCharacters="4" SearchFunc="#SearchItems"
ToStringFunc="#(i => i==null ? null : $"{i.Id} [{i.Name}]")"
SelectValueOnTab="true"/>
on the #bind-Value, Visual studio shows this error
...cannot convert from 'string' to 'EditModel.Item'
This is how I solved it for now...
My SearchItems method now just returns a list of string
protected async Task<IEnumerable<string>> SearchItems(string value)
I've put this attribute in the MudAutocomplete
ToStringFunc="#(i => ItemDisplay(i))"
This is my ItemDisplay method
private string ItemDisplay(string itemId)
{
var item = ListItems.FirstOrDefault(i => i.Id == itemId);
return item == null ? "!Not Found!" : $"{item.Id} [{item.Name}]";
}
I've had to add this to my ComponentBase, to 'cache' all the ListItems for use in ItemDisplay() method:
public List<ListItem> ListItems { get; set; } = new();
In OnInitializedAsync()
ListItems = await MyService.GetItemsAsync();
I've set up my GetItemsAsync() to use IMemoryCache (Microsoft.Extensions.Caching.Memory), but I still don't like this approach. I find it difficult to believe that this component does not support the feature.
Maybe the component is updated but I was able to achieve this by using the following approach which I think is good.
The model you want to use
record State(Guid Id, string Name);
The binding value
private State value1;
The search function returns IEnumerable<State>
private async Task<IEnumerable<State>> Filter(string value)
{
// Filtering logic
}
Finally, I am using ToStringFunc to define how values are displayed in the drop-down list
<MudAutocomplete T="State" ToStringFunc="#(state => state.Name)" Label="US States" #bind-Value="value1" SearchFunc="#Filter" Variant="Variant.Outlined"/>
I have a certain table in the database that stores the following objects:
public partial class Invoice
{
public string DocumentNumber { get; set; }
public DateTime? DocumentDate { get; set; }
public string DocumentReference { get; set; }
public string SerialNumber { get; set; }
public string ProductCode { get; set; }
public string Description { get; set; }
public string Certificate { get; set; }
public string Language { get; set; }
public string Email { get; set; }
}
I also have a query that returns me the number of specific elements:
SELECT Count(*)
FROM (
SELECT DocumentNumber,DocumentDate,DocumentReference
FROM vInvoiceSwivelInfoWeb
WHERE Email = 'someemail#gmail.com' AND Language = 'FR'
GROUP BY DocumentNumber,DocumentDate,DocumentReference
) AS T
The answer looks something like this:
How to use EF to make such a request and get a numerical answer?
I tried like this:
_context.Database.ExecuteSqlRawAsync($"..some SQL query..")
but I do not get the expected result.
UPD: Having received the answer about the impossibility of fulfilling this request through EF, the following question reasonably arose: Is it possible to make this request using LINQ?
You can Leverage ADO.NET via the Context.Database property.
Unfortunately, there is no way to get the count from the database using EF Core execute methods if you have a custom query that is not related to your entities.
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "SELECT Count(*) From Table1";
context.Database.OpenConnection();
using (var result = command.ExecuteReader())
{
// do something with result
}
}
for Updated question
var count = from a in _context.vInvoiceSwivelInfoWeb
where a.Email == "someemail#gmail.com" && a.Language == "FR"
group new { a.DocumentNumber , a.DocumentReference , a.DocumentDate } by a into g
select g.Count()
also, it's important to know which version of EF-Core are you using:
currently, if you are using EF-Core 3 group-by doesn't translate to SQL command so you have to do it on client-side:
check this link :
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client
for EF-Core 3.0 - 3.1.1
var count = _context.vInvoiceSwivelInfoWeb
.Where(a => a.Email == "someemail#gmail.com" && a.Language == "FR" ).ToList()
.GroupBy(a => new { a.DocumentNumber ,a.DocumentDate, a.DocumentReference }).Count();
There is something I don't fully understand with the code bellow. I'm trying to find the workers that have a duplicate module in their modules collection.
Here's the entities (simplified for the sake of brevity):
public class Worker
{
public int Id { get; private set; }
public ICollection<TakenTrainingModule> TakenTrainingModules { get; private set; }
public Worker()
{
TakenTrainingModules = new HashSet<TakenTrainingModule>();
}
}
public class TakenTrainingModule
{
public int Id { get; set; }
public int TrainingModuleId { get; set; }
}
And here's the query:
var query = from worker in _context.Workers.Include(worker => worker.TakenTrainingModules)
let distinctModules = worker.TakenTrainingModules.Select(module => module.TrainingModuleId).Distinct()
where worker.TakenTrainingModules.Count != distinctModules.Count()
select worker;
With the query bellow, the returned workers have their TakenTrainingModules collection empty.
But, with the next query (without using the keywork let), the collection is fully and correctly loaded:
var query = from worker in _context.Workers.Include(worker => worker.TakenTrainingModules)
where worker.TakenTrainingModules.Count != worker.TakenTrainingModules.Select(module => module.TrainingModuleId).Distinct().Count()
select worker;
What am I missing? Is the let keyword first executing the distinct query and fools the object state manager that the module are loaded but they aren't and the selector doesn't load them next?
Any explanations welcome! :-)
I've updated the query as this:
var query = from worker in _context.Workers
let distinctModules = worker.TakenTrainingModules.Select(module => module.TrainingModuleId).Distinct()
where worker.TakenTrainingModules.Count != distinctModules.Count()
select worker;
return query.Include(worker => worker.TakenTrainingModules).ToArray();
And everything is fine. Thanks to Dismissile for pointing me out a nice answer.
Given the follow data class,
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
}
how can I get a sub-query into the ItemNames field?
My current query of
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
};
doesn't seem to be doing the trick...
The data structure is
MenuHeaders MenuItems
----------- ---------
ID ID
HeaderName <-(FK)--MenuHeaderID
MenuItemName
I ended up just changing from a List to IEnumerable. This fixed it.
Wouldnt you want to just put a where in your sub-select to filter that down to all the menu items with the MenuHeaderID equals mh.HeaderName. You can just .Equals() with the StringComparison type if you want as well.
Here is an example...
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName where mhi.MenuHeaderID = mh.HeaderName).ToList<string>()
};
My guess is that your not initiliazing the list within your class. I basing this off the experience I was having with Nhibernate.
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
public EmployeeMenu()
{
ItemNames=new List<string>();
}
}
Hope this helps.
Okay. Try replacing
(from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
by
mh.MenuItems
.AsEnumerable()
.Select(mhi => mhi.MenuItemName)
.ToList()
I question if you want a where clause in there somewhere, but this should get you past the runtime exception.
Any time you see an error message of the form "LINQ to Entities does recognize the method ... and this method can not be translated into a store expression" LINQ to Entities is telling you that it can't figure out how to translate part of the expression tree into a SQL statement. This means you need to pull things client side so that LINQ to Entities doesn't try to translate something that it can't translate.
I have an object containing a date and a count.
public class Stat
{
public DateTime Stamp {get; set;}
public int Count {get; set ;}
}
I have a Serie object that holds a list of thoses Stat plus some more info such as name and so on...
public class Serie
{
public string Name { get; set; }
public List<Stat> Data { get; set; }
...
}
Consider that I have a List of Serie but the series don't all contain the same Stamps.
I need to fill in the missing stamps in all series with a default value.
I thought of an extension method with signature like this (please provide better name if you find one :) ) :
public static IEnumerable<Serie> Equalize(this IEnumerable<ChartSerie> series, int defaultCount)
this question seems to treat the same problem, but when querying directly the DB. of course I could loop through the dates and create another list. But is there any more elegant way to achieve this?
i.e.:
Serie A:
01.05.2010 1
03.05.2010 3
Serie B:
01.05.2010 5
02.05.2010 6
I should get :
Serie A :
01.05.2010 1
02.05.2010 0
03.05.2010 3
Serie B:
01.05.2010 5
02.05.2010 6
03.05.2010 0
Not sure if this is elegant enough for you ;-) but since I like Linq, this is what I would have done (using your naming scheme):
public static IEnumerable<Serie> Equalize(
this IEnumerable<Serie> series,
int defaultCount)
{
var allStamps = series
.SelectMany(s => s.Data.Select(d => d.Stamp))
.Distinct()
.OrderBy(d => d)
.ToList();
return series.Select(serie => new Serie(
serie.Name,
allStamps.Select(d =>
serie.Data.FirstOrDefault(stat => stat.Stamp == d)
??
new Stat(d, defaultCount))
));
}
For this code to compile, your classes needs a couple of constructors:
public class Stat
{
public Stat() {}
public Stat(DateTime stamp, int count)
{
Stamp = stamp;
Count = count;
}
public DateTime Stamp { get; set; }
public int Count { get; set; }
}
public class Serie
{
public Serie() {}
public Serie(string name, IEnumerable<Stat> data)
{
Name = name;
Data = new List<Stat>(data);
}
public string Name { get; set; }
public List<Stat> Data { get; set; }
}
When calling series.Equalize(0) the code above will leave the original instances intact, and return a sequence of newly created Serie-instances with their Data padded with defaults.
Nothing magic about it. Just the sweetness of Linq... (and the null coalescing operator!)
I haven't tried this with loads and loads of data, so your milage may vary.