I am currently building a custom MVC widget for a Sitefinity (v9.2) project.
As part of this widget there is a query to the database to retrieve a collection of a dynamic module type (articles). I am trying to get article types that contain all the labels in my Labels.PublicLabels guid list.
My current query is:
var collection = dynamicModuleManager.GetDataItems(articleType)
.Where(a => a.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live
&& a.Visible == true
&& Labels.PublicLabels.All(l => a.GetValue<IList<Guid>>("Public").Contains(l)));
At runtime I get an exception mentioning that 'server side not implemented'.
Could this be a limitation of OpenAccess?
I have tried a wide range of LINQ to SQL query combinations which have been successful, I am struggling to understand the problem here.
Any ideas would be greatly appreciated!
UPDATE:
I have tried a few variations on the same query such as:
var collection = dynamicModuleManager.GetDataItems(articleType)
.Where(a => a.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live
&& a.Visible == true
&& Labels.PublicLabels.Any(l => a.GetValue<IList<Guid>>("Public").Contains(l)));
No result, I still recieve the following exception message:
Execution of 'System.Linq.Enumerable:Any(IEnumerable1,Func2)' on the database server side currently not implemented.
Any further advice would be greatly appreciated, for now I will assume this is to do with OpenAccess limitations around LINQ to SQL.
Managed to solve this issue. Definitely down to Open Access LINQ to SQL limitations. What I did was add an additional where clause after filtering the collection down as much as I needed to. Here is the complete query:
var collection = dynamicModuleManager.GetDataItems(articleType).Where(a => a.Status == ContentLifecycleStatus.Live && a.Visible == true)
.OrderByDescending(a => a.PublicationDate)
.Distinct()
.Where(a => new HashSet<Guid>(a.GetValue<IList<Guid>>("Public")).IsSupersetOf(Labels.PublicLabels));
I will be reporting this to Sitefinity and if they manage to put a proper fix in place I will report back.
Thank you everyone for your support!
I had the same problem before, this way worked for me
var listIds = Labels.PublicLabels;
var collection = dynamicModuleManager.GetDataItems(articleType)
.Where(a => a.Status == ContentLifecycleStatus.Live && a.Visible == true)
.Where(a => a.GetValue<TrackedList<Guid>>("Public").Where(al => listIds.Contains(al)).Any());
I didn't compile that, let me know if it will not work for you
Related
I have looked at a lot of similar questions but none could give me a solution so I am thinking if anyone can help me with this problem. I have a hierarchy of entities as Clients have multiple ClientRateDeals and then I am trying to fetch only those clients that have a list of client rate deals that all pass some condition. Here's my LINQ query that generating an error :
var query = _context.Client.Where(c=>c.Disabled==false)
.GroupJoin(_context.ClientRateDeal.Where(crd=>crd.Disabled==false),
c => c.Id,
crd => crd.ClientId,
(c, crd) => new
{
c,
crd = crd.Where(cr => cr.DateEnd == null || cr.DateEnd > DateTime.Today)
})
.Where(res => res.crd.Count() == 0)
.Select(cl => cl.c).AsNoTracking().ToList();
as you can see in the result selector argument I have kept that condition and then a where clause on the result selector to fetch only those whose client rate deal whose count is 0. However due to some reason I am getting the exception that the LINQ cant be translated. Can anyone help me with this ?
For unknown reason (it has nothing in similar with GroupBy), LINQ GroupJoin operator is not supported in EF Core 3.x, 5.x.
You have to use one of the available alternatives - (1) collection navigation property (preferred) or (2) correlated subquery.
e.g.
(1) In Client class define
public ICollection<ClientRateDeal> ClientRateDeals { get; set; }
and use it inside the query
var query = _context.Client
.Where(c => c.Disabled==false)
// vvv
.Where(c => !c.ClientRateDeals.Any(
crd => crd.Disabled == false &&
(crd.DateEnd == null || crd.DateEnd > DateTime.Today)))
.AsNoTracking().ToList();
or (2)
var query = _context.Client
.Where(c => c.Disabled==false)
// vvv
.Where(c => !_context.ClientRateDeal.Any(crd =>
c.Id == crd.ClientId &&
crd.Disabled == false &&
cr.DateEnd == null || cr.DateEnd > DateTime.Today))
.AsNoTracking().ToList();
In general, instead of
db.As.GroupJoin(db.Bs, a => a.Id, b => b.AId, (a, Bs) => new { a, Bs })
use
db.As.Select(a => new { a, Bs = db.Bs.Where(b => a.Id == b.AId) })
Related github issue (please go vote in order to have a chance to get that implemented):
Query with GroupBy or GroupJoin throws exception #17068
Query: Support GroupJoin when it is final query operator #19930
even though the second is not exactly what we need (we want just GroupJoin to be translated as it was written in correlated subquery syntax shown above).
Visual studio - MVC Core2 using Microsoft ASP.NET Controller
I am trying to filter records based on a user and if its marked for deletion (bool data type)
I am using the && operator and trying to order by dates column, however its not working
Here is my code
var DataContext = _context.Shops_Basket.Include(c => c.products)
.Where(c => c.Username == user && c.IsDeleted == 0)).Orderby dates desc;
Your OrderBy is wrong in the above. Also the error you are getting sounds like a type-name collision to me. Try to start your variables with lower case letter to avoid this:
var dataContext = _context.Shops_Basket
.Include(c => c.products)
.Where(c => c.Username == user
&& !c.IsDeleted)
.OrderByDescending(c => c.dates);
Orderby dates desc is not valid c#. Probably you are confused by SQL. You need to use OrderByDescending:
var dataContext = _context.Shops_Basket.Include(c => c.products)
.Where(c => c.Username == user && c.IsDeleted == 0)
.OrderByDescending(c => c.dates);
Also note, we have naming conventions in C#. Local variables are starting with a lower character. Underscores like Shops_Basket we usualy do not use. Instead the better DbSet name would be ShopsBasket.
C# on .NET 3.5 Framework
So, I'm trying to make an efficient query to find all emails to purge from my system. What should be purged are emails that have been attempted (Datetime column LastAttempted is not null) and the # of days to retain (int column RetentionDays) has elapsed. This works, but I know it's pulling everything back and filtering in memory.
This is what I have right now
var emails = dbContext.Emails
.Where(x => x.LastAttempted.HasValue == true)
.ToList()
.Where(x => ((DateTime)x.LastAttempted).AddDays(x.RetentionDays) <= DateTime.Now);
How can I update this so it only pull the records I'm caring about from SQL Server?
Use EntityFunctions.AddDays to add days to a DateTime in an EF query.
var emails = dbContext.Emails
.Where(x => x.LastAttempted.HasValue &&
EntityFunctions.AddDays(x.LastAttempted, x.RetentionDays)
<= DateTime.Now);
This part of query:
dbContext.Emails
.Where(x => x.LastAttempted.HasValue == true)
.ToList();
will be executed in the sql server and an in memory collection will be returned, when ToList is called.
Then the last part of your query:
.Where(x =>
((DateTime)x.LastAttempted).AddDays(x.RetentionDays) <= DateTime.Now);
will be executed in the items of the above mentioned in memory collection.
So the problem relies on the last part of your query. You could avoid this using the method AddDays of EntityFunctions and avoid making the second filtering in the in memory collection. This can be done like below:
dbContext.Emails
.Where(x => x.LastAttempted.HasValue == true &&
EntityFunctions.AddDays(x.LastAttempted, x.RetentionDays)
<= DateTime.Now);
Move ToList() at the end of the query
var now = DateTime.Now;
var emails = dbContext.Emails
.Where(x => x.LastAttempted.HasValue &&
EntityFunctions.AddDays((DateTime)x.LastAttempted, x.RetentionDays) <= now).
ToList();
You can always query the database directly, like this:
var items = dbContext.Database.SqlQuery<Email>("select * from Emails where LastAttempted Is Not Null And LastAttempted + RetentionDaya >= GetDate()", new SqlParameter[0]);
NOTE: I made some guesses with respect to your entity, table, and column names.
I have some issues wrapping my head around the .include in LINQ. I'd appreciate a lot if someone could show me the query to achieve this:
I have two entities, User and Validation (which is a serie of validation codes) . They are related in EF5 as 1:*. I'm trying to get a User object and eager load the Validations collection.
At this moment, I'm doing this (cx being the Context inside a using block):
var user = cx.Users
.Where(u => u.UserGuid.Equals(UserGuid))
.FirstOrDefault();
if (user == null)
{
return 0;
}
var validation = cx.Validations
.Where(v => v.Code.Equals(Code))
.Where(v => v.UserId.Equals(user.UserId))
.FirstOrDefault();
But how do I achieve this in only one query so that then I can use
user.Validations.FirstOrDefault();
without getting an error and by only getting the Validations that satisfy the Validation.Code == Code test?
Thanks for your help (I know I'm sounding confused but that's because I am).
Did you try something like this:
var validation = cx.Validations.Where(v=>v.Code==Code &&
cx.Users.Any(u=>u.UserID==v.UserID &&
u.UserGuid==UserGuid)
).FirstOrDefault();
You might need to include the related records as well by doing:
var user = cx.Users
.Where(u => u.UserGuid.Equals(UserGuid))
.Include(u => u.Validations)
.FirstOrDefault();
but then you should be able to select the requested validaiton with
user.Validations
.FirstOrDefault(v => v.Code.Equals(Code));
All
Please help me make include() work in the following case:
ctx.Messages
.Include("Comments.CommentType")
.Include("Comments.Owner")
.Include("Comments.Message")
.Where(m => m.RID == messageId)
.SelectMany(m => m.Comments)
.Where(c => c.CommentType.Id == commentTypeId)
.ToList();
How I should rewrite this query?
P.S. I'm using EF 3.5 (not 4.0)
This is most likely related to an issue with Include and joins. Basically it comes down to this: Includes are only applied at the end of the statement, and due to joining, the type of your query changes from an IQueryable<Message> to an IQueryable<Comment>.
By removing the join, it should correctly include the navigation properties. Try the following:
ctx.Comments
.Include("CommentType")
.Include("Owner")
.Include("Message")
.Where(c => c.Message.RID == messageId && c => c.CommentType.Id == commentTypeId)
.ToList();