从队列中的所有用户运行方法

时间:2012-07-26 06:01:24

标签: c# asp.net

我的设置:用户有一个页面,他执行一些操作和数据转换 我将这些操作存储在数据集中(在会话中)  然后,在按钮单击时,我需要执行一些更多转换并调用自定义数据库函数,该函数将每个数据表中的每一行插入到数据库中。我已经解决了所有这些问题并且效果非常好。

但是,现在,我需要做的是修改按钮单击事件不要立即运行,而是将该操作放入队列(这是所有用户的公共队列)。因此,如果user1单击该按钮,则user2单击该按钮,然后我们需要获取user1数据集并在其上运行方法,并且仅在此之后获取user2数据集并在其上运行方法。所有的一个队列。

private void btnclick()
{
  DataSet ds = Session["test"] as DataSet;
  PerformFinalTransformation(ds);
  foreach (DataTable dt in ds.Tables)
  {
     foreach (DataRow dr in dt.Rows)
     {
        CallCustomSqlFunction(dr);
     }
  }
}

我想避免在可能的情况下将数据集存储在数据库中,因为它们对于每个用户(表/列的数量)并不相同。我正在寻找关于如何在asp.net / c#中完成此任务的任何指示。 (石英网是我应该考虑的吗?)。我应该寻找什么概念?并行编程?还是asp.net队列?或者是其他东西?甚至无法启动,因为不知道该从什么开始。顺便说一句,如果有所帮助,我会在我的应用程序中使用最新的DotNetNuke。

7 个答案:

答案 0 :(得分:2)

  1. 要重新启动,您必须将队列存储在数据库中。您可以通过将每个数据集序列化为xml并将生成的xml存储在nvarchar(max)列中来克服数据差异。

  2. 运行队列的最佳方法是使用单独的Windows服务,一次选择一个项目并进行处理。您可以对IIS执行相同的操作(只需在Application_Start中启动另一个线程),但我不建议将其作为不可扩展的(并且需要重新启动IIS,池再循环等)。

答案 1 :(得分:2)

从架构的角度来看,您的解决方案很好。队列是要走的路。但是,解决方案并非易事。需要考虑的一些事情:

  1. 恢复能力:如果系统崩溃或重启会怎样?队列是否被保留?
  2. 你将如何从这个队列消费?你的ASP.Net应用程序中的一个线程(丑陋)? Windows服务?
  3. 并发性怎么样?
  4. 根据你的答案,我给你一些选择:

    1. 内存中的队列,使用[ConcurrentQueue][1],在一个线程中上升。可能使用Singleton来启用对队列的访问。我不建议你这样做。

    2. 创建服务,通过WCF公开接口并从ASP.Net应用程序访问它。该服务也应该包含[ConcurrentQueue][2],访问数据库等将是服务职责(您仍然会遇到崩溃和数据丢失的问题)。

    3. 使用MSMQ。还有一些服务来封装从ASP.Net接收调用的功能(如果你不想让ASP.Net直接访问MSMQ)并访问数据库(MSMQ调用这个服务)。

    4. 这些只是我的头脑。请随时发表评论,我会尽可能回答(我现在正在工作)。

答案 2 :(得分:0)

由于我对这些问题知之甚少,WCF和MSMQ都是很好的解决方案,在你的场景中肯定会有所帮助。如果你做得好,编写你想要的数据库实现并不是很糟糕。您可以在每次插入后将字段设置为AutoIncrement,将数据添加到数据库中。

然而,要使用队列来解决这个问题,下面是几个指南和示例:

一些示例项目实施:

希望你发现它们有用。祝你好运。

答案 3 :(得分:0)

是否可以使用table value parameter提交数据,而不是为每一行调用函数将其插入数据库?这是SQL Server 2008及更高版本的一项功能,它允许您在单个语句中完全提交表。

使用这种解决方案,您可能不需要队列。我发现当我切换到表参数时,我的代码在性能上有了很大提高,因此没有必要批处理语句。

以下是您的代码的工作方式:

private void btnclick()
{
    DataSet ds = Session["test"] as DataSet;
    PerformFinalTransformation(ds);
    using (SqlConnection conn = new SqlConnection(my_connection_string)) {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand("insert_multiple_tables", conn)) {
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter param = new SqlParameter("@table0", SqlDbType.Structured);
            param.Value = ds.Tables[0];
            cmd.Parameters.Add(param);
            SqlParameter param = new SqlParameter("@table1", SqlDbType.Structured);
            param.Value = ds.Tables[1];
            cmd.Parameters.Add(param);
            cmd.ExecuteNonQuery();
        }
    }
}

然后,您可以像这样定义存储过程:

CREATE TYPE udt_mytable0 AS TABLE (
    col1 int,
    col2 varchar(255),
    col3 datetime,
    -- and so on
)

CREATE TYPE udt_mytable1 AS TABLE (
    col1 bigint
)

CREATE PROCEDURE insert_multiple_tables
    @mytable0 udt_mytable0 READONLY,
    @mytable1 udt_mytable1 READONLY
AS

INSERT INTO mydatabase0
    (col1, col2, col3)
SELECT 
    col1, col2, col3
FROM 
    @mytable0

INSERT INTO mydatabase1
    (col1)
SELECT 
    col1
FROM 
    @mytable1

答案 4 :(得分:0)

如果我应该这样做,我可能最终会采用以下方式:

队列存储

如果您不想将队列保留在内存中(这可能是个坏主意,请参阅其他答案)您需要在服务器上安装某种数据库或其他存储,而这些存储在服务器重置时未清除和那样的东西。为此,您当然可以使用像SQL Server这样的传统数据库,但也可以使用其他选项。

如果您已经在服务器上没有SQL Server或类似的商业数据库,我建议使用像RavenDB这样的文档数据库,或者只是将队列存储为一系列带有时间戳的文件(XML序列化)(请记住在文件名中使用某种随机性,这样你就不会得到两个具有相同时间戳的文件,这是不太可能的,但它会发生)。这两种情况都存在问题,但我认为没有完美的解决方案,因此可以归结为您希望用户使用您的服务的频率。

清空队列

根据您使用的队列的存储类型,我只需将控制台应用程序设置为每5分钟运行一次的服务器上的计划任务,或者运行所需的频率。计划任务检查队列中是否有新作业,并继续处理最高作业。

<强>结论

以上描述是“简单”的方法。它不需要很多技能,也不需要你进入一种全新的框架和类似的东西。

如果是我的话,我会选择文件和计划任务的解决方案,因为它很容易实现和测试。如果您遇到大量流量,您可以随时更改存储队列的方式,使其更加友好。

作为最后一句话:记得在从队列处理作业时进行记录以帮助您进行调试;)

答案 5 :(得分:0)

我认为你应该看看并行库中的“任务”......

http://msdn.microsoft.com/en-us/library/dd537609.aspx

处理asinc队列非常好。

虽然我建议你使用某种持久性来避免数据丢失,不管是在db中的另一个表中,还是在文件或任何其他方法中序列化。

您不应该只信任内存存储。 IIS重置或Windows重启,即使在appPool中回收也会破坏未处理的数据。

答案 6 :(得分:-1)

我认为您可以使用缓存来实现这一目标。创建包含每个会话数据的队列,并将队列存储在缓存中。然后使用缓存执行自定义数据库操作。当您使用队列时,它将是FCFS。因此,只有在使用用户1之后才会使用用户2的数据。