C# 使用yield-return实现数据库队列
我正在重构一个遗留应用程序。该应用程序使用SQL Server数据库表对由一个或多个Windows服务检索和处理的作业进行排队。我想创建一个迭代器,在保持正确的锁的同时,将处于“等待”状态的下一个排队作业提取出来进行处理。下面包括一个单元测试示例。我的问题是,我的方法是否有任何潜在的阻碍因素C# 使用yield-return实现数据库队列,c#,.net,sql-server-2008,queue,yield-return,C#,.net,Sql Server 2008,Queue,Yield Return,我正在重构一个遗留应用程序。该应用程序使用SQL Server数据库表对由一个或多个Windows服务检索和处理的作业进行排队。我想创建一个迭代器,在保持正确的锁的同时,将处于“等待”状态的下一个排队作业提取出来进行处理。下面包括一个单元测试示例。我的问题是,我的方法是否有任何潜在的阻碍因素 // Database DDL if object_id('Jobs') is not null begin drop table Jobs; end go create table Jobs (
// Database DDL
if object_id('Jobs') is not null begin
drop table Jobs;
end
go
create table Jobs
(
Id int identity(1,1) not null primary key clustered
, JobStatus varchar(50) not null
);
insert Jobs
select 'Waiting'
union all
select 'Waiting'
union all
select 'Processing'
union all
select 'Completed'
union all
select 'Failed';
// Unit Test
// Data Model
public sealed class Job
{
public readonly int JobId;
public Job(int jobId)
{
JobId = jobId;
}
}
[TestFixture]
public class JobsTest
{
private const string connectionString =
"Data Source=.;Initial Catalog=<databasename>;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False";
const string SQL =
@"declare @jobId table(JobId int)
update top(1) Jobs
set JobStatus = 'Processing'
output Inserted.Id into @jobId
where JobStatus = 'Waiting'
select JobId from @jobId;";
[Test]
public void CanIterateJobs()
{
foreach (var job in Jobs)
{
Assert.NotNull(job, "job was null.");
Console.WriteLine(job.JobId);
}
}
public static IEnumerable<Job> Jobs
{
get
{
while (true)
{
Job job = null;
do
{
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand(SQL, connection))
{
connection.Open();
var reader = command.ExecuteReader();
if (reader.Read())
{
job = new Job(Convert.ToInt32(reader["JobId"]));
yield return job;
}
}
}
} while (job == null);
Task.Delay(1000);
}
}
}
}
//数据库DDL
如果对象id(“作业”)不为空,则开始
删除表格作业;
结束
去
创建表作业
(
Id int标识(1,1)不为空主键
,JobStatus varchar(50)不为空
);
插入作业
选择“等待”
联合所有
选择“等待”
联合所有
选择“处理”
联合所有
选择“已完成”
联合所有
选择“失败”;
//单元测试
//数据模型
公开密封班工作
{
公共只读int-JobId;
公共作业(int jobId)
{
JobId=JobId;
}
}
[测试夹具]
公营职业培训
{
私有常量字符串连接字符串=
“数据源=;初始目录=;集成安全性=True;连接超时=15;加密=False;TrustServerCertificate=False”;
常量字符串SQL=
@“声明@jobId表(jobId int)
更新前(1)个作业
设置作业状态='正在处理'
将.Id输出插入@jobId
其中JobStatus=‘Waiting’
从@JobId;“;中选择JobId;
[测试]
公共职位空缺()
{
foreach(作业中的var作业)
{
Assert.NotNull(作业,“作业为空”);
Console.WriteLine(job.JobId);
}
}
公共静态可数作业
{
得到
{
while(true)
{
Job=null;
做
{
使用(var连接=新的SqlConnection(connectionString))
{
使用(var命令=新的SqlCommand(SQL,连接))
{
connection.Open();
var reader=command.ExecuteReader();
if(reader.Read())
{
job=新作业(将.ToInt32(读卡器[“JobId”]);
收益回报工作;
}
}
}
}while(job==null);
任务延迟(1000);
}
}
}
}
Task.Delay的调用在那里没有任何作用。调用本身只是启动一个任务,该任务将在一秒钟内完成;它不会在一秒钟内阻塞线程,你需要线程。为此,睡眠。当你产生你的下一个作业时,你就完成了给定的命令/连接,但在请求下一个之前,你不会真正处理它们。这意味着您在调用方处理给定项的整个时间内都在保留这些资源。您可以通过将屈服
移动到do
/的末尾之后,而
,轻松解决此问题。请注意,这不会影响程序的正确性,只是有助于更有效地利用系统资源。@RedPolygon老实说,我不知道它在那里做什么。也许它只是为了调试的目的而存在,以代表真正的工作,我不知道。在任何情况下,这都是一个无用的操作,可能应该删除。代码复查问题->移动。此问题似乎与主题无关,因为它是代码复查