Select all with EF and lambda - linq

I wrote this awesome code that I am having trouble using now.
Currently working example:
complete item = ReliableExecution.RetryWithExpression<complete, complete>(u => u.FirstOrDefault(x => x.str_en == segment));
and this is a part of the RetryWithExpression code:
public static TValue RetryWithExpression<T, TValue>(Func<ObjectSet<T>, TValue> func, Int32 retryInfiniteLoopGuard = 0)
where T : class
{
RetryPolicy policy = RetryPolicyProvider.GetSqlAzureRetryPolicy();
using (DDEntities dataModel = new DDEntities())
{
var entitySet = dataModel.CreateObjectSet<T>();
...
var query = policy.ExecuteAction(() => (func(entitySet)));
...
Now my question is how to change the above select query to do SELECT * ?
I did try this but it tells me things about errors I don't understand:
complete item = ReliableExecution.RetryWithExpression<complete, complete>(u => u.Select(x => x.str_en != ""));

IQueryable<complete> items = ReliableExecution.RetryWithExpression<complete, IQueryable<complete>>(u => u.Where(x => x.str_en != "")); ?

Related

EF Core ToDictionary throw expression could not be translated

I have this query:
Project = await Context.Projects.Where(x => x.Id == project.Id)
.Select(x => new ProjectModel
{
Id = project.Id,
Name = project.Name,
TreeDataDict = x.Tasks.Select(y => new TreeItemModel()
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
}).ToDictionary(y => y.NodeId, y => y)
}).SingleOrDefaultAsync();
and also tried like this:
Project = await Context.Projects.Where(x => x.Id == project.Id)
.Select(x => new ProjectModel
{
Id = project.Id,
Name = project.Name,
TreeDataDict = x.Tasks.ToDictionary(y => y.Id, y => new TreeItemModel(
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
})
}).SingleOrDefaultAsync();
Both ways I got this exception:
What could be causing this? and is there a way I could make this work without fetching as a list and then covert it to dictionary? What could be the most efficient way to achieve this?
Regards
What could be causing this?
Translation of the nested ToDictionary call (none of the available overloads) is not supported. Consider it one of the (many) current (latest at the time of writing official v5.0.11) EF Core shortcomings.
Interestingly though, the Dictionary<TKey, TValue> constructor overload with IEnumerable<KeyValuePair<TKey, TValue>> argument as well as projection (Select) to KeyValuePair<TKey, TValue> is supported, which gives the workaround - replace the ToDictionary call with the aforementioned constructor and Select, e.g. (replace int with the type of the Id)
TreeDataDict = new Dictionary<int, TreeItemModel>(
x.Tasks.Select(y => new TreeItemModel()
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
})
.Select(e => new KeyValuePair<int, TreeItemModel>(e.NodeId, e)));
This will fix the current error. But note that you are using other non-translatable constructs (custom method calls like SetTaskParentId(y, y.Type) and TaskHelper.GetTaskIcon(y.Type)) which are supported only in the final Select, so make sure to not add LINQ operator like Where, OrderBy etc. after the root query Select(x => new ProjectModel { ... }, otherwise you'll get other runtime errors. ToList, `First

Entity Framework Core 3.1 - Linq - Could not be translated. Either rewrite the query in a form that can be translated

When I try to use a select in a Linq query and create a viewmodel in the select statement, it works. When I try to extract the creation of the viewmodel to a static method, it gives the following error: "Could not be translated. Either rewrite the query in a form that can be translated". Why is this happening?
The calling of SiteViewModel.GetSiteViewModel(s) generates this error.
public async Task<IEnumerable<ContractViewModel>> ExecuteAsync(CancellationToken token = new CancellationToken())
{
var contracts = await _domainContext.Contracts.Include(s => s.Sites)
.AuthorizedFor(User)
.Select(c => new ContractViewModel()
{
CreationDate = c.CreationDate,
ContractId = c.Id,
ContractLineWiatingForConfirmation = c.ContractLines.Any(l => l.Status == ContractLineStatus.WaitingForConfirmation),
ModificationDate = c.ModificationDate,
ExpirationDate = c.ExpirationDate,
Title = c.Title,
Status = c.Status,
Sites = c.Sites.Select(s => SiteViewModel.GetSiteViewModel(s)).OrderBy(s => s.SiteName)
})
.ToListAsync(token);
return contracts;
}
GetSiteViewModel looks like this:
public static SiteViewModel GetSiteViewModel(ContractSite x)
{
return new SiteViewModel
{
SiteId = x.Site.Id,
SiteCode = x.Site.Code,
SiteName = x.Site.Name,
AgressoCode = x.Site?.ExternalReference?.Identifier,
AgressoAdministration = x.Site?.ExternalReference?.Source
};
}
Update:
When I remove the orderby, it works. When I remove the static method to create a viewmodel, and place the creation of the viewmodel in the query itself and DONT remove the orderby, it then also works...
It is because LINQ translator can not look into your GetSiteViewModel method body and recognize how properties are remapped.
Simplest fix, is to do OrderBy before GetSiteViewModel call
Sites = c.Sites.OrderBy(s => s.Site.Name).Select(s => SiteViewModel.GetSiteViewModel(s))
But if it is not an option, then you can solve that by several methods.
Simplest and without third-party dependencies is to change your function to return IQueryable
public static class MyDtoExtensions
{
public static IQueryable<SiteViewModel> GetSiteViewModel(this IQueryable<ContractSite> query)
{
return query.Select(x => new SiteViewModel
{
SiteId = x.Site.Id,
SiteCode = x.Site.Code,
SiteName = x.Site.Name,
AgressoCode = x.Site?.ExternalReference?.Identifier,
AgressoAdministration = x.Site?.ExternalReference?.Source
};
}
}
Then you can use this extension:
Sites = c.Sites.AsQueryable().GetSiteViewModel().OrderBy(s => s.SiteName)
But for better extensibility I would suggest to use this extension (similar functionality is coming with LINQKit)
https://github.com/axelheer/nein-linq/
And rewrite your function
public static class MyDtoExtensions
{
[InjectLambda]
public static SiteViewModel GetSiteViewModel(this ContractSite x)
{
_getSiteViewModel =?? GetSiteViewModel().Compile();
return _getSiteViewModel(x);
}
private static Func<ContractSite, SiteViewModel> _getSiteViewModel;
private static Expression<Func<ContractSite, SiteViewModel>> GetSiteViewModel()
{
return x => new SiteViewModel
{
SiteId = x.Site.Id,
SiteCode = x.Site.Code,
SiteName = x.Site.Name,
AgressoCode = x.Site?.ExternalReference?.Identifier,
AgressoAdministration = x.Site?.ExternalReference?.Source
};
}
}
Then you can use GetSiteViewModel in queries without limitations. But do not forget to call ToEntityInjectable()
public async Task<IEnumerable<ContractViewModel>> ExecuteAsync(CancellationToken token = default)
{
var contracts = await _domainContext.Contracts
.ToEntityInjectable()
.Include(s => s.Sites)
.AuthorizedFor(User)
.Select(c => new ContractViewModel()
{
CreationDate = c.CreationDate,
ContractId = c.Id,
ContractLineWiatingForConfirmation = c.ContractLines.Any(l => l.Status == ContractLineStatus.WaitingForConfirmation),
ModificationDate = c.ModificationDate,
ExpirationDate = c.ExpirationDate,
Title = c.Title,
Status = c.Status,
Sites = c.Sites.Select(s => s.GetSiteViewModel()).OrderBy(s => s.SiteName)
})
.ToListAsync(token);
return contracts;
}

Can't get IEnumerable type using linq filtered query

i have been finding problems related to the following code..Actually i want to select filtered records but it gives me 0 records. I have tried the following. Please Help me..
public static IEnumerable<PostModel> GetPostData(string cat)
{
var data = new LinqClassDataContext(); IEnumerable<PostModel> pm;
pm=data.PostTables.Where(Post => new PostModel
{ CategoryName= Post.CategoryName}.Equals(cat)).Select
(Post => new PostModel
{ PostID = (int)Post.PostID,
PostSubject = Post.PostSubject,
PostAuthor = Post.PostAuthor,
PostDate = Post.PostDate,
PostContent = Post.PostContent,
CategoryName = Post.CategoryName
});
}
Why your PostModel class object would be Equals string cat?
Maybe you meant:
data.PostTables.Where(Post => Post.CategoryName == cat)
Even though you overrided Equals method on PostModel I think you should use line I meantioned above because it is more obvious.
Also at the end of query you should call .ToList() method for initiating it. And you should dispose context after creating it in the method.
public static IEnumerable<PostModel> GetPostData(string cat)
{
var data = new LinqClassDataContext();
var pm = data.PostTables.Where(post => post.CategoryName == cat)
.Select(Post => new PostModel
{
PostID = (int)Post.PostID,
PostSubject = Post.PostSubject,
PostAuthor = Post.PostAuthor,
PostDate = Post.PostDate,
PostContent = Post.PostContent,
CategoryName = Post.CategoryName
})
.ToList();
data.Dispose();
return pm;
}

NHibernate Linq ToFuture and Max

I want the following two queries to be executed in single round trip to database so I'm using ToFutureValue.
var query = AsQueryable()
.Where(x => x.User == user);
var singinCount = query
.Where(x => x.ActionDate >= from && x.ActionDate <= to)
.ToFutureValue(q => q.Count());
var lastSignin = query
.Where(x => x.ActionName.ToLower() == Actions.Signin))
.ToFutureValue(q => q.Max(x => x.ActionDate));
The problem is that Count and Max returns result but not IQueryable. I have found the following extension that enables me to pass Count to ToFutureValue:
public static IFutureValue<TResult> ToFutureValue<TSource, TResult>(
this IQueryable<TSource> source, Expression<Func<IQueryable<TSource>, TResult>> selector)
where TResult : struct
{
var provider = (INhQueryProvider)source.Provider;
var method = ((MethodCallExpression)selector.Body).Method;
var expression = Expression.Call((Expression)null, method, source.Expression);
return (IFutureValue<TResult>)provider.ExecuteFuture(expression);
}
Now I need to adopt that extension for Max because I have to pass arguments to Max (.ToFutureValue(q => q.Max(x => x.ActionDate))). How can I do that?
as a workaround
var lastSignin = query
.Where(x => x.ActionName.ToLower() == Actions.Signin))
.OrderByDescending(x => x.ActionDate)
.Take(1)
.ToFutureValue();
It looks like there is appropriate patch in GitHub pull request
#120
And here is jira bug NH-3184
the following is code from pull #120 that works:
public static IFutureValue<TResult> ToFutureValue<T, TResult>(
this IQueryable<T> query, Expression<Func<IQueryable<T>, TResult>> selector)
{
var nhQueryable = query as QueryableBase<T>;
if (nhQueryable == null)
{
throw new NotSupportedException("Query needs to be of type QueryableBase<T>");
}
var provider = (INhQueryProvider) query.Provider;
var expression = ReplacingExpressionTreeVisitor.Replace(
selector.Parameters.Single(),
query.Expression,
selector.Body);
return (IFutureValue<TResult>) provider.ExecuteFuture(expression);
}

LINQ: Group By + Where in clause

I'm trying to implement a T-SQL equivalent of a where in (select ...) code in LINQ.
This is what I have now:
int contactID = GetContactID();
IEnumerable<string> threadList = (from s in pdc.Messages
where s.ContactID == contactID
group 1 by new { s.ThreadID } into d
select new { ThreadID = d.Key.ThreadID}).ToList<string>();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group new { s } by new { s.ThreadID } into d
let maxMsgID = d.Where(x => x.s.ContactID != contactID).Max(x => x.s.MessageID)
select new {
LastMessage = d.Where(x => x.s.MessageID == maxMsgID).SingleOrDefault().s
};
However, my code won't compile due to this error for the ToList():
cannot convert from
'System.Linq.IQueryable<AnonymousType#1>'
to
'System.Collections.Generic.IEnumerable<string>'
Anyone have any suggestions on how to implement this? Or any suggestions on how to simplify this code?
Your query returns a set of anonymous types; you cannot implicitly convert it to a List<string>.
Instead, you should select the string itself. You don't need any anonymous types.
Change it to
var threadList = pdc.Messages.Where(s => s.ContactID == contactID)
.Select(s => s.ThreadID)
.Distinct()
.ToList();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group s by s.ThreadID into d
let maxMsgID = d.Where(x => x.ContactID != contactID).Max(x => x.MessageID)
select new {
LastMessage = d.Where(x => x.MessageID == maxMsgID).SingleOrDefault()
};

Resources