Correct interpretation of SQL request by EF Core - linq

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();

Related

How to Return class with list using LINQ in C#?

I have one "Profiles" and "Addresses" table Profiles table contain one record of person and Addresses table contains its multiple address.
I want to write LINQ query to return single person record with its multiple address in one Query. So I have created one custom class with "profileId" ,"firstName" and list of address.
I need a output like this please help.
1 (profileId)
Mark (firstName)
------Sanday (address one)
------Stronsay (address two)
------Foula (address three)
public class profileDetails
{
public int profileIdData { get; set; }
public string firstNameData { get; set; }
public List<lstAddress> lstAddressData { get; set; }
}
public class lstAddress
{
public int addressId { get; set; }
public string address { get; set; }
}
//This is my query
var result = (from profile in DbContext.Profiles
join address in DbContext.Addresses
on profile.profileId equals address.addressId
select new profileDetails()
{
profileIdData = profile.profileId,
firstNameData = profile.firstName,
lstAddressData = new { address.addressId, address.address }.toList()
}
)
Have you tried using the Include method? Something like this:
var result = (from profile in DbContext.Profiles
join address in DbContext.Addresses
on profile.profileId equals address.addressId).Include("lstAddressData");
(the lstAddressData name may be incorrect in my example above, it refers to the reference property you want to populate with address items)

Count the occurrences of an object in a list where multiple criteria are matching using LINQ

I have a list of Cutdetails. I am trying to write a function using LINQ that will return the count of bars in the list where the CODE , BRAND, CODE and LENGTH all match. I want to be able to specify all these parameters and return a number for the number of matches.
I have tried using foreach statements which is fine but i'm sure there is an neater and smarter way to do it using LINQ. Any suggestions?
List<Bar> bars = new List<Bar>();
public class Bar
{
public string Brand { set; get; }
public string System { set; get; }
public string Code { set; get; }
public string Length { set; get; }
}
Thanks in advance!
Will
You can filter using the match and then do a count.
var occurences = bars.Where(x => x.Brand == "Brand" && x.Code == "code").Count();

LINQ query returning a List<> as a class member

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.

Linq to NHibernate projection to anon. type results in mystifying cast error

I have an TaxWork entity which is persisted using NHibernate. This entity has the following properties (among others):
public virtual TaxWorkType Type { get; set; } //Kctc.TaxWorkType is an enumeration
public virtual TaxWorkStatus Status { get; set; } //Kctc.TaxWorkStatus is an enumeration
public virtual LegalWorkPriority Priority { get; set; } //Kctc.LegalWorkType is an enumeration
public virtual User Handler { get; set; } //Kctc.BusinessLayer.Entities.User is another entity
public virtual int? CaseNumber { get; set; }
I am using Linq to NHibernate to pull of a subset of the tax work objects as follows (taxWorkRepository.All obviously returns an IQueryable):
foreach (TaxWork taxWork in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated))
{
...
}
This works fine. I want to use projection in order to query only the columns that are required in this case. I am usnig the following code:
foreach (var taxWorkFragment in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated).Select(x => new { Type = x.Type, DateCreated = x.DateCreated, Handler = x.Handler, Status = x.Status, Priority = x.Priority }))
{
...
}
However, I'm getting the following error when trying to create the anonymous type:
Invalid cast from 'Kctc.TaxWorkStatus' to 'Kctc.BusinessLayer.Entities.User'.
Where on earth is it getting the idea that it should be casting a TaxWorkStatus to a User?
Any suggestions whatsoever what might be going wrong?
Try to make like this:
foreach (var taxWorkFragment in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated)
.Select(x => new TaxWork { Type = x.Type, DateCreated = x.DateCreated, Handler = x.Handler, Status = x.Status, Priority = x.Priority }))
{
...
}
It should help

LinqToSQl and the Member access not legal on type exception

The basic problem...
I have a method which executes the following code:
IList<Gig> gigs = GetGigs().WithArtist(artistId).ToList();
The GetGigs() method gets Gigs from my database via LinqToSql...
So, when GetGigs().WithArtist(artistId).ToList() is executed I get the following exception:
Member access 'ListenTo.Shared.DO.Artist Artist' of 'ListenTo.Shared.DO.Act' not legal on type 'System.Collections.Generic.List`1[ListenTo.Shared.DO.Act]
Note that the extension function "WithArtist" looks like this:
public static IQueryable<Gig> WithArtist(this IQueryable<Gig> qry, Guid artistId)
{
return from gig in qry
where gig.Acts.Any(act => (null != act.Artist) && (act.Artist.ID == artistId))
orderby gig.StartDate
select gig;
}
If I replace the GetGigs() method with a method that constructs a collection of gigs in code (rather than from the DB via LinqToSQL) I do NOT get the exception.
So I'm fairly sure the problem is with my LinqToSQl code rather than the object structure.
However, I have NO IDEA why the LinqToSQl version isnt working, so I've included all the associated code below. Any help would be VERY gratefully receivced!!
The LinqToSQL code....
public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
return from g in DBContext.Gigs
let acts = GetActs(g.ID)
join venue in DBContext.Venues on g.VenueID equals venue.ID
select new ListenTo.Shared.DO.Gig
{
ID = g.ID,
Name = g.Name,
Acts = new List<ListenTo.Shared.DO.Act>(acts),
Description = g.Description,
StartDate = g.Date,
EndDate = g.EndDate,
IsDeleted = g.IsDeleted,
Created = g.Created,
TicketPrice = g.TicketPrice,
Venue = new ListenTo.Shared.DO.Venue {
ID = venue.ID,
Name = venue.Name,
Address = venue.Address,
Telephone = venue.Telephone,
URL = venue.Website
}
};
}
IQueryable<ListenTo.Shared.DO.Act> GetActs()
{
return from a in DBContext.Acts
join artist in DBContext.Artists on a.ArtistID equals artist.ID into art
from artist in art.DefaultIfEmpty()
select new ListenTo.Shared.DO.Act
{
ID = a.ID,
Name = a.Name,
Artist = artist == null ? null : new Shared.DO.Artist
{
ID = artist.ID,
Name = artist.Name
},
GigId = a.GigID
};
}
IQueryable<ListenTo.Shared.DO.Act> GetActs(Guid gigId)
{
return GetActs().WithGigID(gigId);
}
I have included the code for the Act, Artist and Gig objects below:
public class Gig : BaseDO
{
#region Accessors
public Venue Venue
{
get;
set;
}
public System.Nullable<DateTime> EndDate
{
get;
set;
}
public DateTime StartDate
{
get;
set;
}
public string Name
{
get;
set;
}
public string Description
{
get;
set;
}
public string TicketPrice
{
get;
set;
}
/// <summary>
/// The Act object does not exist outside the context of the Gig, therefore,
/// the full act object is loaded here.
/// </summary>
public IList<Act> Acts
{
get;
set;
}
#endregion
}
public class Act : BaseDO
{
public Guid GigId { get; set; }
public string Name { get; set; }
public Artist Artist { get; set; }
}
public class Artist : BaseDO
{
public string Name { get; set; }
public string Profile { get; set; }
public DateTime Formed { get; set; }
public Style Style { get; set; }
public Town Town { get; set; }
public string OfficalWebsiteURL { get; set; }
public string ProfileAddress { get; set; }
public string Email { get; set; }
public ImageMetaData ProfileImage { get; set; }
}
public class BaseDO: IDO
{
#region Properties
private Guid _id;
#endregion
#region IDO Members
public Guid ID
{
get
{
return this._id;
}
set
{
this._id = value;
}
}
}
}
I think the problem is the 'let' statement in GetGigs. Using 'let' means that you define a part of the final query separately from the main set to fetch. the problem is that 'let', if it's not a scalar, results in a nested query. Nested queries are not really Linq to sql's strongest point as they're executed deferred as well. In your query, you place the results of the nested query into the projection of the main set to return which is then further appended with linq operators.
When THAT happens, the nested query is buried deeper into the query which will be executed, and this leads to a situation where the nested query isn't in the outer projection of the query to execute and thus has to be merged into the SQL query ran onto the DB. This is not doable, as it's a nested query in a projection nested inside the main sql query and SQL doesn't have a concept like 'nested query in a projection', as you can't fetch a set of elements inside a projection in SQL, only scalars.
I had the same issue and what seemed to do the trick for me was separating out an inline static method call that returned IQueryable<> so that I stored this deferred query into a variable and referenced that.
I think this is a bug in Linq to SQL but at least there is a reasonable workaround. I haven't tested this out yet but my assumption is that this problem may arise only when referencing static methods of a different class within a query expression regardless of whether the return type of that function is IQueryable<>. So maybe it's the class that holds the method that is at the root of the problem. Like I said, I haven't been able to confirm this but it may be worth investigating.
UPDATE: Just in case the solution isn't clear I wanted to point it out in context of the example from the original post.
public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
var acts = GetActs(g.ID); // Don't worry this call is deferred
return from g in DBContext.Gigs
join venue in DBContext.Venues on g.VenueID equals venue.ID
select new ListenTo.Shared.DO.Gig
{
ID = g.ID,
Name = g.Name,
Acts = new List<ListenTo.Shared.DO.Act>(acts),
Description = g.Description,
StartDate = g.Date,
EndDate = g.EndDate,
IsDeleted = g.IsDeleted,
Created = g.Created,
TicketPrice = g.TicketPrice,
Venue = new ListenTo.Shared.DO.Venue {
ID = venue.ID,
Name = venue.Name,
Address = venue.Address,
Telephone = venue.Telephone,
URL = venue.Website
}
};
}
Note that while this should correct the issue at hand there also seems to be another issue in that the deferred acts query is being accessed in each element of the projection which I would guess would cause separate queries to be issued to the database per row in the outer projection.
I don't see anything in your classes to indicate how LINQ to SQL is meant to work out which column is which, etc.
Were you expecting the WithArtist method to be executed in .NET, or converted into SQL? If you expect it to be converted into SQL, you'll need to decorate your Gig class with appropriate LINQ to SQL attributes (or configure your data context some other way). If you want it to be executed in code, just change the first parameter type from IQueryable<Gig> to IEnumerable<Gig>.
I found out that an issue like this (which I also had recently) can be resolved, if you convert the IQueryable (or Table) variable Gigs into a list like so
return from g in DBContext.Gigs.ToList()
...
If that still doesn't work, do the same for all the IQueryables. The reason behind seems to me that some queries are too complex to be translated into SQL. But if you "materialize" it into a list, you can do every kind of query.
Be careful, you should add "filters" (where conditions) early because too much memory consumption can become a problem.

Resources