【问题标题】:Why does AsEnumerable not work when using FromSqlRaw为什么使用 FromSqlRaw 时 AsEnumerable 不起作用
【发布时间】:2021-11-08 08:44:13
【问题描述】:

我目前正在创建一个网站,在数据表中展示我的所有患者,我必须使用 FromSqlRaw 才能从我的数据库中获取数据。我有一个搜索功能,可以让我在表格中搜索患者,但是在进入页面时,当我使用 AsQueryable 并且表格中没有显示任何数据时,我会收到此错误。它建议我使用 AsEnumerable 但是当我这样做时,我得到一个智能感知错误。关于如何解决的任何想法?

          public async Task<IActionResult> Search(StaySearchViewModel model)
    {
        if (model.Cleared)
        {
            return Json(new
            {
                draw = model.Draw,
                data = new object[] { },
                recordsFiltered = 0,
                recordsTotal = 0,
                total = 0
            });
        }

        var records = getSearchData(model);
            //var records = System.Linq.Enumerable.AsEnumerable(getSearchData(model)); // Hard coding this an enumerable will  break line 55, 57, and 64
            //Sorting
            if (!string.IsNullOrEmpty(model.SortOrder))

                records = records.OrderBy(model.SortOrder);

            var count = await records.CountAsync().ConfigureAwait(false);

            records = records.Skip(model.Start);
            if (model.Length != -1) records = records.Take(model.Length);


            // Create models
            var result = new List<SpStaySearchResultViewModel>();
        try
        {
            await records.ForEachAsync(r =>
            {
                result.Add(new SpStaySearchResultViewModel()
                {
                    BuildingName = r.BuildingName,
                    CaseManager = r.CaseManager,
                    CaseManagerId = r.CaseManagerId,
                    OccupantFileAs = r.OccupantFileAs,
                    StayOCFSNumber = r.StayOCFSNumber,
                    StayId = r.StayId,
                    MaxOfBillSentDate = r.MaxOfBillSentDate,
                    CountOfChildren = r.CountOfChildren,
                    StartDate = r.StartDate,
                    EndDate = r.EndDate,
                    OccupantId = r.OccupantId,
                    IsActive = r.IsActive,


                });
            }).ConfigureAwait(false);
        }
        catch (Exception e) { }
            return Json(new
            {
                draw = model.Draw,
                data = result,
                recordsFiltered = count,
                recordsTotal = count,
            });

    }
    private IQueryable<spStaysSearch> getSearchData(StaySearchViewModel model)
    {

            var records = db.SpStaySearches.FromSqlRaw("dbo.spStaysSearch").AsQueryable();

            if (model.OccupantId.HasValue)
                records = records.Where(x => x.OccupantId == model.OccupantId);

            if (!string.IsNullOrWhiteSpace(model.OccupantFileAs))
                records = records.Where(x => x.OccupantFileAs == model.OccupantFileAs);

            if (!string.IsNullOrWhiteSpace(model.BuildingName))
                records = records.Where(x => x.BuildingName == model.BuildingName);

            if (!string.IsNullOrWhiteSpace(model.CaseManager))
                records = records.Where(x => x.CaseManager == model.CaseManager);

            if (!string.IsNullOrWhiteSpace(model.BuildingName))
                records = records.Where(x => x.BuildingName == model.BuildingName);

            if (model.IntakeDateStart.HasValue && model.IntakeDateEnd.HasValue)
            {
                records = records.Where(x => x.StartDate >= model.IntakeDateStart && x.StartDate <= model.IntakeDateEnd);
            }
            else
            {
                if (model.IntakeDateStart.HasValue)
                    records = records.Where(x => x.StartDate >= model.IntakeDateStart);
                if (model.IntakeDateEnd.HasValue)
                    records = records.Where(x => x.StartDate <= model.IntakeDateEnd);
            }

            if (model.ExitDateStart.HasValue && model.ExitDateEnd.HasValue)
            {
                records = records.Where(x => x.EndDate >= model.ExitDateStart && x.EndDate <= model.ExitDateEnd);
            }
            else
            {
                if (model.ExitDateStart.HasValue)
                    records = records.Where(x => x.EndDate >= model.ExitDateStart);
                if (model.ExitDateEnd.HasValue)
                    records = records.Where(x => x.EndDate <= model.ExitDateEnd);
            }

            if (model.IsActive.HasValue)
                records = records.Where(x => x.IsActive == model.IsActive);

        return records;
    }

【问题讨论】:

  • 有两个命名空间具有扩展方法名称AsEnumerable()。检查您是否使用命名空间并删除一个多余的。如果两者都在使用,则应使用静态样式调用AsEnumerable 方法
  • 另外,不要发布图片。复制该弹出窗口中的错误文本。
  • 你为什么打电话给var records = getSearchData(model);,然后跟records = db.SpStaySearch.FromSqlRaw("dbo.SpStaySearch").AsEnumerable();?这将覆盖原始结果。 AsEnumerable() 将脱离 IQueryable 实现,代码似乎假定它会对其进行操作。这看起来像您在尝试使用直接 SQL 语句破解的现有 Linq 查询表达式时遇到了问题。在被FromRawSqlAsEnumerable 捆绑之前 是否存在错误或问题?
  • @LeVu 你说得对,我需要同时使用命名空间并尝试使我的搜索功能静态异步在我的 Json & var records = getSearchData(model); 上产生 CS0120 错误我正在使用 System.Linq 和 System.Linq.Dynamic.Core
  • @StevePy 我更新了我的代码并将 var records = db.SpStaySearch.FromSqlRaw("dbo.SpStaySearch").AsEnumerable();在我的 getSearchData 函数中,但我得到了同样的错误。以前我遇到数据库操作错误。 SqlException:无效的对象名称“SpStaySearch”。 Web 工具告诉我,为了应用我的迁移,我需要更新我 PMC 中的数据库,但我仍然遇到同样的错误。

标签: c# .net asp.net-mvc entity-framework


【解决方案1】:

试试这个

 var records = getSearchData(model).ToList();
 var count =  records.Count;
.....

您不能按 model.SortOrder 对记录进行排序,因为它与记录无关。 你只能做这样的事情

 if (!string.IsNullOrEmpty(model.SortOrder)) records = records.OrderBy(r=> r.Id);

【讨论】:

  • 我更新了我的代码,我遇到的问题是:System.InvalidOperationException: 'FromSqlRaw' 或 'FromSqlInterpolated' 是用不可组合的 SQL 调用的,并且有一个查询在它上面组成。考虑在客户端执行组合的方法之后调用“AsEnumerable”。我相信我的 var count = await records.CountAsync().ConfigureAwait(fase);可能是罪魁祸首,但我不知道如何解决。
【解决方案2】:

因为您的源数据是一个存储过程,所以您不能在它之上编写额外的查询表达式。相反,您必须按照错误提示,通过枚举结果集将其加载到内存中。

Including Related Data
SQL Server 不允许组合存储过程调用,因此任何尝试将其他查询运算符应用于此类调用都会导致 SQL 无效。在 FromSqlRaw 或 FromSqlInterpolated 方法之后使用 AsEnumerable 或 AsAsyncEnumerable 方法,以确保 EF Core 不会尝试对存储过程进行组合。

在代码中解释这一点的明显方法是对来自 SP 的结果调用 .ToList(),然后为了匹配您现有的代码模式,您可以将该结果转换回 IQueryable

var records = db.SpStaySearches.FromSqlRaw("dbo.spStaysSearch")
                               .ToList()
                               .AsQueryable()

使用AsEnumerable() 有时会出现问题,因为您可能已经实现了许多不同的库,它们都可能提供AsEnumerable() 扩展方法。

我们必须这样做,因为即使在 SQL 中,您也不能简单地从 SP 中选择然后向其添加 where 子句,您首先必须将结果读入临时表或表变量,然后你可以从结果集中重新查询,这就是我们现在正在做的事情,我们正在将结果读入 C# 变量 (.ToList()),然后在该结果的顶部组成一个内存查询。

如果您的搜索逻辑必须封装在存储过程中,那么鉴于技术限制,通常的期望是您将搜索参数作为可选参数添加到存储过程中,而不是在C# 中的结果。

我们可以帮助您将过滤器逻辑移动到dbo.spStaysSearch,但您必须发布该 SP 的内容,最好是作为一个新问题。


完全不使用 SP,因为我们几乎失去了 EF 可以为我们提供的所有优点,另一种方法是用原始 SQL 完全替换您的 SP,然后您的其余逻辑将按预期工作。

var sql = @"
SELECT 
    tblStays.*, tblOccupant.OccupantID, 
    tblOccupant.FileAs AS OccupantFileAs, 
    IIF(tblStays.BuildingName LIKE 'Main Shelter',
           tblOccupant.OCFSMainNumber,
           tblOccupant.OCFSNorthNumber) AS StayOCFSNumber, 
    COALESCE([CountOfOccupantStayID], 0) AS CountOfChildren, 
    tblCaseManager.FileAs AS CaseManager,
    StaysMaxBillSentDate.MaxOfBillSentDate
FROM tblStays 
LEFT JOIN tblOccupantStays ON tblStays.StayID = tblOccupantStays.StayID
LEFT JOIN tblOccupant ON tblOccupantStays.OccupantID = tblOccupant.OccupantID
LEFT JOIN (
    SELECT lkpOccStays.StayID
         , COUNT(tblOccupantStays.OccupantStayID) AS CountOfOccupantStayID
    FROM tblOccupantStays lkpOccStays 
    INNER JOIN tblOccupant lkpChild ON lkpOccStays.OccupantID = lkpChild.OccupantID
    WHERE lkpChild.OccupantType LIKE 'Child'
    GROUP BY lkpOccStays.StayID
) OccupantStays_CountOfChildren ON tblStays.StayID = OccupantStays_CountOfChildren.StayID
LEFT JOIN tblCaseManager ON tblStays.CaseManagerID = tblCaseManager.CaseManagerID
LEFT JOIN (SELECT tblStayBillingHx.StayID
         , MAX(tblStayBillingHx.BillSentDate) AS MaxOfBillSentDate
    FROM tblStayBillingHx 
    GROUP BY tblStayBillingHx.StayID
) StaysMaxBillSentDate ON tblStays.StayID = StaysMaxBillSentDate.StayID
";

var records = db.SpStaySearches.FromSqlRaw(sql);

SP 以这种方式提供结果集的结构,如果您使用 Database-First 方法但您根本不再执行 SP,这可能是必要的。

此答案中的 SQL 仅作为语法指南提供,没有足够的信息来确定查询的有效性或结果是否符合您的业务要求。

【讨论】:

  • 你指的是数据模型中的列还是SP的DBSet?我将如何链接该新帖子以便您找到它?
  • 不,我的意思是 SP SQL 脚本,您需要修改 SP 以接受参数并对其进行处理,另一种选择是用 EF/Linq 查询完全替换 SP,然后您可以在代码中管理它。将 SP 执行到内存中然后应用过滤器的解决方案通常是一种反模式,会导致性能不佳。只需发布新问题的链接作为对此评论的回复;)
  • @Masterolu 现在看到你的SP,我不得不问你为什么要使用SP?用 linq 编写会更简单。
  • 我真的不知道,我问了我的团队,他们说如果我想在 Linq 中做,我可以
猜你喜欢
  • 1970-01-01
  • 2014-05-25
  • 2021-11-14
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-24
  • 1970-01-01
相关资源
最近更新 更多