【问题标题】:Entity Framework Core 3.1 - Linq - Could not be translated. Either rewrite the query in a form that can be translatedEntity Framework Core 3.1 - Linq - 无法翻译。以可以翻译的形式重写查询
【发布时间】:2020-12-11 19:50:43
【问题描述】:

当我尝试在 Linq 查询中使用 select 并在 select 语句中创建视图模型时,它可以工作。当我尝试将视图模型的创建提取到静态方法时,会出现以下错误:“无法翻译。要么以可翻译的形式重写查询”。为什么会这样?

调用 SiteViewModel.GetSiteViewModel(s) 会产生此错误。

    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 如下所示:

    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
        };
    }

更新:

当我删除 orderby 时,它可以工作。当我删除创建视图模型的静态方法,并将视图模型的创建放在查询本身中并且不要删除 orderby 时,它也可以工作......

【问题讨论】:

    标签: linq asp.net-core entity-framework-core


    【解决方案1】:

    这是因为 LINQ 翻译器无法查看您的 GetSiteViewModel 方法体并识别属性是如何重新映射的。

    最简单的解决方法是在GetSiteViewModel 调用之前执行OrderBy

    Sites = c.Sites.OrderBy(s => s.Site.Name).Select(s => SiteViewModel.GetSiteViewModel(s))
    

    但如果它不是一个选项,那么你可以通过几种方法来解决它。

    最简单且没有第三方依赖的方法是更改​​你的函数以返回 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
          };
       }
    }
    

    那么你就可以使用这个扩展了:

    Sites = c.Sites.AsQueryable().GetSiteViewModel().OrderBy(s => s.SiteName)
    

    但为了更好的可扩展性,我建议使用这个扩展(LINQKit 提供了类似的功能) https://github.com/axelheer/nein-linq/

    重写你的函数

    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
            };
        }
    }
    

    然后,您可以在查询中使用GetSiteViewModel,不受限制。不过别忘了拨打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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-21
      • 1970-01-01
      • 2021-11-22
      • 2021-10-13
      • 2020-06-16
      • 2021-09-10
      • 2018-12-29
      • 1970-01-01
      相关资源
      最近更新 更多