【问题标题】:The method 'System.String TrimStart(Char[])' is only supported in LINQ to Entities方法 'System.String TrimStart(Char[])' 仅在 LINQ to Entities 中受支持
【发布时间】:2019-01-22 03:19:05
【问题描述】:

最近,当我在项目中发现问题时,我一直在重构我们的一些笨重且无法正常工作的 LINQ 查询。这是一个让我陷入循环的问题。我必须承认,我是一名使用实体框架进入 C# 的 android 开发人员,所以我并不真正了解这里发生了什么。尽管如此,这些查询对我来说看起来几乎相同(在此处添加一个子查询并在此处添加一个新连接)。第一种方法将返回没有错误:

public virtual IQueryable<ScratchGameDetailsModel> GetScratchGameDetails(string reconGUID)
    {
        var lotteryReconHeader = _lotteryReconHeaderRepository.GetAll().AsEnumerable(); // Changed on Nov 7,2017
        var lotteryReconDetails = _lotteryReconDetailRepository.GetAll().Where(s => s.ReconGUID == reconGUID).AsEnumerable();
        var lotteryGames = _lotteryGameRepository.GetAll().AsEnumerable();
        var lotteryActivations = _lotteryActivationRepository.GetAll().AsEnumerable();


        var response = (from ltrd in lotteryReconDetails
                        join lg in lotteryGames on ltrd.GameGUID equals lg.GameGUID
                        join la in lotteryActivations on lg.GameGUID equals la.GameGUID into LotteryActivations_join
                        from la in LotteryActivations_join.DefaultIfEmpty()
                        join ltrh in lotteryReconHeader on ltrd.ReconGUID equals ltrh.ReconGUID // Added on Nov 7,2017
                        select new ScratchGameDetailsModel()
                        {
                            ReconGUID = ltrd.ReconGUID,
                            GameGUID = ltrd.GameGUID,
                            GameNumber = lg.GameNumber ?? Constants.Zero,
                            GameName = lg.GameName ?? String.Empty,
                            GameUPC = lg.GameUPC ?? string.Empty,
                            GameBin = lg.GameBin ?? Constants.Zero,
                            StartCount = ltrd.StartCount,
                            EndCount = ltrd.EndCount,
                            CashierSales = ltrd.CashierSales,
                            ExpectedStartCount = ltrd.ExpectedStartCount,
                            QtyOnRoll = lg.QtyOnRoll ?? Constants.Zero,
                            BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber.Split(new char[] { '-' }, 2).ElementAt(1).TrimStart('-')),
                            ReconTimeStamp = ltrh.ReconTimeStamp    // Added on Nov 7,2017
                        });

        return response.AsQueryable();
    }

您可能会说出我为什么不喜欢这种方法,其中 3 个可枚举列表是稍后加入的数据库表的完整转储。当数据库增长时,这些变量的查询时间会增加。如果你问我,完全浪费资源。由于大量的变量声明,我已经修复了一些需要 5 分钟以上才能在另一个表上填充数据的方法,现在它们正在

这是一个替换方法,除了现在包含一个子查询外,它应该返回所有相同的方法。它也不再设置 ienumerable 列表,而只是使用 DB 上下文处理查询。这会引发错误说明:

只有在没有指定修剪字符作为参数时,LINQ to Entities 才支持方法“System.String TrimStart(Char[])”。

public virtual IQueryable<ScratchGameDetailsModel> GetScratchGameDetailsNew(string reconGUID)
    {
        var newResponse = (from ltrd in _context.LotteryReconDetail
                           join lg in _context.LotteryGame on ltrd.GameGUID equals lg.GameGUID
                           join la in _context.LotteryActivation
                                on new
                                {
                                    lg.GameGUID,
                                    BookNumber =
                                    ((from la in _context.LotteryActivation
                                      where la.GameGUID == lg.GameGUID
                                      orderby la.row_id descending
                                      select new { la.BookNumber }).FirstOrDefault().BookNumber)
                                }
                                equals new { la.GameGUID, la.BookNumber } into la_join
                           from la in la_join.DefaultIfEmpty()
                           join ltrh in _context.LotteryReconHeader on ltrd.ReconGUID equals ltrh.ReconGUID
                           where ltrd.ReconGUID == reconGUID
                           select new ScratchGameDetailsModel()
                           {
                               ReconGUID = ltrd.ReconGUID,
                               GameGUID = ltrd.GameGUID,
                               GameNumber = lg.GameNumber ?? Constants.Zero,
                               GameName = lg.GameName ?? String.Empty,
                               GameUPC = lg.GameUPC ?? string.Empty,
                               GameBin = lg.GameBin ?? Constants.Zero,
                               StartCount = ltrd.StartCount,
                               EndCount = ltrd.EndCount,
                               CashierSales = ltrd.CashierSales,
                               ExpectedStartCount = ltrd.ExpectedStartCount,
                               QtyOnRoll = lg.QtyOnRoll ?? Constants.Zero,
                               BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber.Split(new char[] { '-' }, 2).ElementAt(1).TrimStart('-')),
                               ReconTimeStamp = ltrh.ReconTimeStamp
                           });

        return newResponse.AsQueryable();
    }

使用我的方法,如果我使用相同的操作(如设置变量)并将这些操作用于查询,我会收到“对象引用未设置为对象的实例”。尤里卡!但为什么?相同的原始 SQL 查询没有问题,我非常想知道 LINQ 中使用 DB 上下文与可查询/可枚举上下文的区别。

我还修改了这些变量声明并使用 AsQueryable() 而不是 AsEnumerable() 所以整个列表不会被转储,而是用作查询中的数据库上下文。如果我没记错的话,这就是可枚举和可查询之间的显着区别。

正如@TheGeneral 指出的那样已解决。调用分配已更新以根据需要处理值修剪。

BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber),

然后更新调用方法来处理修剪

var scratchGameDetails = _lotteryReconService.GetScratchGameDetailsNew(reconGUID).ToList().Select(s => new
            {
                s.ReconGUID,
                s.GameGUID,
                s.GameNumber,
                s.GameName,
                s.GameUPC,
                s.GameBin,
                s.StartCount,
                s.EndCount,
                s.CashierSales,
                s.ExpectedStartCount,
                RangeStart = gameStartCount,
                RangeEnd = useBaseZero ? (s.QtyOnRoll - 1) : s.QtyOnRoll,
                BookNumber = (s.BookNumber == string.Empty ? string.Empty : s.BookNumber.Split(new char[] { '-' }, 2).ElementAt(1).TrimStart('-')),
                s.ReconTimeStamp
            }).OrderByDescending(x => x.ReconTimeStamp).Distinct();

【问题讨论】:

  • 你认为AsEnumerable 会做什么?
  • 我的假设是 TrimStart() 在 SQL 中被转换为 LTRIM,因此无法处理“trimchars”参数的实际值。但这只是一个猜测。这是一个较旧的,但提到了这一点:stackoverflow.com/questions/7936718/…
  • @mjwillis,据我了解,它会根据从查询中检索到的数据创建一个枚举列表。之前使用的查询在执行任何其他查询功能之前将整个表转储到内存中。

标签: c# entity-framework linq


【解决方案1】:
  • 一个正在使用内存数据(存储库代码(mega sigh))

  • 另一个将投影推回数据库,这是正在破坏的数据库

它抛出这个异常的全部原因是因为数据库不理解string.Trim() 的概念,Split 也会抛出

但是,我们可以使用任何String Canonical Functions.. 虽然最简单的方法是项目,因为您需要在内存中处理字符串操作

BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber,

请注意,我们可以将LTRIM 用作EntityFunctions,但它只会修剪空间。再次看到String Canonical Functions

【讨论】:

  • 谢谢@TheGeneral。是的,我不是一个 c# 人(还),但不难发现这个变量是一个胖内存猪。你可以看到我为什么要重写其中的一些方法。如您所示,我设置了 BookNumber,并在调用此服务的方法中处理了 TrimStart()。一切都按预期运行。使用分辨率更新问题。
猜你喜欢
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 2017-08-08
  • 1970-01-01
  • 1970-01-01
  • 2013-04-07
  • 1970-01-01
  • 2012-05-03
相关资源
最近更新 更多