5

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

但是,现在,我需要做的是修改按钮单击事件不立即运行,而是将该操作放入队列中(这是所有用户的公共队列)。所以如果 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# 中完成此操作的任何指示。(我应该考虑quartz.net 吗?)。我应该寻找哪些概念?并行编程?或asp.net队列?或者是其他东西?甚至无法开始,因为不知道从什么开始。顺便说一句,如果有帮助的话,我会在我的应用程序中使用最新的 DotNetNuke。

4

7 回答 7

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

  2. 运行队列的最佳方式是使用单独的 Windows 服务,该服务将一次选择一项并处理它。您可以对 IIS 执行相同的操作(只是在 中启动另一个线程Application_Start),但我不建议这样做是不可扩展的(并且受 IIS 重新启动、池回收等影响)。

于 2012-07-30T20:49:10.200 回答
2

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

  1. 弹性:如果系统崩溃或重启怎么办?队列是否保留?
  2. 你将如何从这个队列中消费?ASP.Net 应用程序中的线程(丑陋)?Windows 服务?
  3. 并发呢?

根据你的回答,我给你一些选择:

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

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

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

这些只是我的想法。请随时发表评论,我会尽可能回答(我目前正在工作)。

于 2012-07-30T20:57:37.430 回答
0

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

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

处理 asinc 队列非常好。

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

你不应该只相信内存存储。IIS 重置或 Windows 重新启动,甚至 appPool 中的回收都会破坏您未处理的数据。

于 2012-08-06T18:16:15.257 回答
0

由于我对此类问题知之甚少,WCF 和 MSMQ 是很好的解决方案,绝对可以帮助您解决方案。如果你做得好,写一个你想要的数据库实现也不错。您可以将数据添加到数据库中,并AutoIncrement在每次插入后设置一个字段。

但是,要使用队列解决此问题,以下是一些指南和示例:

一些示例项目实现:

希望你觉得它们有用。祝你好运。

于 2012-07-30T20:58:13.420 回答
0

与其为每一行调用一个函数将其插入数据库,是否可以使用表值参数提交数据?这是 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
于 2012-07-30T21:05:09.360 回答
0

如果我应该这样做,我可能最终会这样做:

队列存储

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

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

清空队列

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

结论

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

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

最后一点:记得在处理队列中的作业时进行日志记录,以帮助您进行调试;)

于 2012-07-30T21:51:02.653 回答
-1

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

于 2012-07-26T08:43:14.973 回答