【发布时间】:2021-06-16 18:09:24
【问题描述】:
我有一个由 MVC 控制器调用的方法,而 MVC 控制器又在 Web 应用程序中使用 Ajax 调用。它曾经看起来像这样:
public static IEnumerable<DepartmentViewModel> GetDepartments()
{
DataContext db = new DataContext(ConfigurationHelper.DepartmentsConnectionString);
string sql = "SELECT DISTINCT RTRIM(DEP_CODE) AS [DepartmentCode], RTRIM(DEP_NAME) AS
[DepartmentName] " +
"FROM [departmentinfo].[dbo].[Dep_School_Faculty] " +
"WHERE [DEPARTMENT_IN_USE] = 'Y' AND [VALID] = 'Y' " +
"ORDER BY [DepartmentName]";
IEnumerable<DepartmentViewModel> departments = db.ExecuteQuery<DepartmentViewModel>(sql);
return departments;
}
SQL 查询需要几秒钟的时间来执行,这就是为什么 Ajax 在加载网页的其余部分后调用它的原因,因此用户可以在加载部门列表的同时继续处理其他内容。这很好用。
不过,现在我需要稍微修改查询,并在将后端数据的结果传递给控制器之前对其进行一些处理,因此我将方法更新为如下所示:
public static async Task<IEnumerable<AcademicAreaViewModel>> AllAcademicAreas()
{
IEnumerable<DepartmentSchoolFacultyModel> deptSchoolFaculty = await GetAllAcademicAreas();
using (DataContext db = new DataContext(ConfigurationHelper.DepartmentsConnectionString))
{
string sql = "SELECT DISTINCT RTRIM(DEP_CODE) AS [DepartmentCode], RTRIM(DEP_NAME) AS [DepartmentName], " +
"RTRIM(SCHOOL_CODE) AS [SchoolCode], RTRIM(SCHOOL_NAME) AS [SchoolName], " +
"RTRIM(FACULTY_CODE) AS [FacultyCode], RTRIM(FACULTY_NAME) AS [FacultyName] " +
"FROM [departmentinfo].[dbo].[Dep_School_Faculty] " +
"WHERE [DEPARTMENT_IN_USE] = 'Y' AND [VALID] = 'Y'";
deptSchoolFaculty = db.ExecuteQuery<DepartmentSchoolFacultyModel>(sql);
};
IEnumerable<AcademicAreaViewModel> departments = deptSchoolFaculty
.GroupBy(d => d.DepartmentCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().DepartmentName,
Code = "Department:" + g.First().DepartmentCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> schools = deptSchoolFaculty
.GroupBy(d => d.SchoolCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().SchoolName,
Code = "School:" + g.First().SchoolCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> academicAreas = departments.Concat(schools);
return academicAreas;
}
然后我遇到的问题是,以IEnumerable<AcademicAreaViewModel> departments = deptSchoolFaculty 开头的行抛出了一个空异常错误,因为deptSchoolFaculty 变量尚未填充数据。
所以,我想,这是一个典型的异步编程用例,恰好发生在我的克星身上(无论我读了多少关于这个主题或实施它,它永远不会在我的脑海中“点击”)。
我这样重构我的代码:
public static async Task<IEnumerable<AcademicAreaViewModel>> AllAcademicAreasAsync()
{
IEnumerable<DepartmentSchoolFacultyModel> deptSchoolFaculty = await GetAllAcademicAreasAsync();
IEnumerable<AcademicAreaViewModel> departments = deptSchoolFaculty
.GroupBy(d => d.DepartmentCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().DepartmentName,
Code = "Department:" + g.First().DepartmentCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> schools = deptSchoolFaculty
.GroupBy(d => d.SchoolCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().SchoolName,
Code = "School:" + g.First().SchoolCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> academicAreas = departments.Concat(schools);
return academicAreas;
}
private static Task<IEnumerable<DepartmentSchoolFacultyModel>> GetAllAcademicAreasAsync()
{
return Task.Run(() =>
{
IEnumerable<DepartmentSchoolFacultyModel> deptSchoolFaculty = new List<DepartmentSchoolFacultyModel>();
using (DataContext db = new DataContext(ConfigurationHelper.DepartmentsConnectionString))
{
string sql = "SELECT DISTINCT RTRIM(DEP_CODE) AS [DepartmentCode], RTRIM(DEP_NAME) AS [DepartmentName] " +
"FROM [departmentinfo].[dbo].[Dep_School_Faculty] " +
"WHERE [DEPARTMENT_IN_USE] = 'Y' AND [VALID] = 'Y' " +
"ORDER BY [DepartmentName]";
return db.ExecuteQuery<DepartmentSchoolFacultyModel>(sql);
}
});
}
不幸的是,尽管我使用await 关键字,但我希望在继续之前等待 SQL 查询的结果,但同一行会引发相同的空异常错误。
首先,我不明白为什么,在我的代码的原始版本中,该方法似乎要等待SQL查询完成才能返回结果(因为结果总是返回到我的页面),但现在我正在尝试以相同的方法处理结果。
其次,虽然我怀疑我的问题的答案可能是使用asynchronous data methods 而不是DataContext.ExecuteQuery,但我想了解为什么我所写的内容不起作用。另外,我更喜欢 ExecuteQuery 的简洁性,因为它可以将 sql 查询的结果直接写入 C# 模型中。
【问题讨论】:
-
问题:为什么是 SQL?这可以很容易地转换为 LINQ。
-
公平的问题。这个数据库不是我的应用程序(我使用 EF)的主数据库,我只为这件事调用它,所以创建一个额外的 ADO 数据模型等等似乎有点过头了。
-
你使用哪个 ORM?
-
如果
db.ExecuteQuery<DepartmentSchoolFacultyModel>(sql)返回IEnumerable,你必须在使用里面调用.ToList(),因为你dispose了DataContext,枚举无法完成。 -
作为旁注,您使用
Task.Run以expose an asynchronous wrapper for a synchronous method,并且您在Web 应用程序中这样做。现在等待斯蒂芬克利里来告诉你为什么this is a bad idea!
标签: c# linq asynchronous async-await