这对于T4模板构建Azure队列来说是好的/首选的模式吗?

3

我正在构建一个 T4 模板,以帮助人们以一致且简单的方式构建 Azure 队列。我希望使其自我记录,并有一定的一致性。

  1. 首先,我将队列名称放在文件顶部,队列名称必须为小写,因此我添加了 ToLower()。

  2. 公共构造函数使用内置的 StorageClient API 访问连接字符串。我已经看到许多不同的方法,希望得到几乎适用于所有情况的解决方案。(有想法吗?请分享)

  3. 我不喜欢不必要的 HTTP 请求来检查队列是否已创建,因此我将其设置为 static bool。我没有实现 Lock(monitorObject),因为我认为这是不需要的。

  4. 与大多数 MSDN 文档使用字符串并使用逗号进行解析不同,我在将对象传递到队列时对其进行序列化。

  5. 为进一步优化,我使用JSON 序列化器扩展方法以充分利用 8k 的限制。不确定编码是否会进一步优化此过程

  6. 添加重试逻辑以处理队列出现的某些情况(请参见 HTML 链接)

  7. Q:DataContext 是否是此类的适当名称?

  8. Q:按我所做的方式命名队列操作名称是否是不良实践?

您认为我应该进行哪些其他更改?

public class AgentQueueDataContext
{
    // Queue names must always be in lowercase
    // Is named like a const, but isn't one because .ToLower won't compile...
    static string AGENT_QUEUE_ACTION_NAME = "AgentQueueActions".ToLower();

  static bool QueuesWereCreated { get; set; }

    DataModel.SecretDataSource secDataSource = null;

    CloudStorageAccount cloudStorageAccount = null;
    CloudQueueClient cloudQueueClient = null;
    CloudQueue queueAgentQueueActions = null;

    static AgentQueueDataContext()
    {
        QueuesWereCreated = false;
    }

    public AgentQueueDataContext() : this(false)
    {
    }
    public AgentQueueDataContext(bool CreateQueues)
    {
        // This pattern of setting up queues is from:
        // ttp://convective.wordpress.com/2009/11/15/queues-azure-storage-client-v1-0/
        //
        this.cloudStorageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
        this.cloudQueueClient = cloudStorageAccount.CreateCloudQueueClient();
        this.secDataSource = new DataModel.SecretDataSource();

        queueAgentQueueActions = cloudQueueClient.GetQueueReference(AGENT_QUEUE_ACTION_NAME);

        if (QueuesWereCreated == false || CreateQueues)
        {
            queueAgentQueueActions.CreateIfNotExist();
            QueuesWereCreated = true;
        }
    }

  // This is the method that will be spawned using ThreadStart
   public void CheckQueue()
    {
        while (true)
        {
            try
            {
                CloudQueueMessage msg = queueAgentQueueActions.GetMessage();

                bool DoRetryDelayLogic = false;

                if (msg != null)
                {
                    // Deserialize using JSON (allows more data to be stored)
                    AgentQueueEntry actionableMessage = msg.AsString.FromJSONString<AgentQueueEntry>();

                    switch (actionableMessage.ActionType)
                    {
                        case AgentQueueActionEnum.EnrollNew:
                            {
                                // Add to 
                                break;
                            }
                        case AgentQueueActionEnum.LinkToSite:
                            {
                                // Link within Agent itself

                                // Link within Site

                                break;
                            }
                        case AgentQueueActionEnum.DisableKey:
                            {
                                // Disable key in site

                                // Disable key in AgentTable (update modification time)

                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }

                    //
                    // Only delete the message if the requested agent has been missing for 
                    // at least 10 minutes
                    //
                    if (DoRetryDelayLogic)
                    {
                        if (msg.InsertionTime != null)
                            if (msg.InsertionTime < DateTime.UtcNow + new TimeSpan(0, 10, 10))
                                continue;

                        // ToDo: Log error: AgentID xxx has not been found in table for xxx minutes.   
                        //                  It is likely the result of a the registratoin host crashing.
                        //                  Data is still consistent.  Deleting queued message.
                    }


                    //
                    // If execution made it to this point, then we are either fully processed, or 
                    // there is sufficent reason to discard the message.
                    //
                    try
                    {
                        queueAgentQueueActions.DeleteMessage(msg);
                    }
                    catch (StorageClientException ex)
                    {
                        // As of July 2010, this is the best way to detect this class of exception
                        // Description: ttp://blog.smarx.com/posts/deleting-windows-azure-queue-messages-handling-exceptions
                        if (ex.ExtendedErrorInformation.ErrorCode == "MessageNotFound")
                        {
                            // pop receipt must be invalid
                            // ignore or log (so we can tune the visibility timeout)
                        }
                        else
                        {
                            // not the error we were expecting
                            throw;
                        }
                    }
                }
                else
                {
                   // allow control to fall to the bottom, where the sleep timer is...
                }
            }
            catch (Exception e)
            {
                // Justification: Thread must not fail.
                //Todo: Log this exception

                // allow control to fall to the bottom, where the sleep timer is...
                // Rationale: not doing so may cause queue thrashing on a specific corrupt entry
            }

            // todo: Thread.Sleep() is bad
            //       Replace with something better...
            Thread.Sleep(9000);
        }

我有点困惑 - T4在这个问题中应该从何说起? - GarethJ
@Garethj 我打算使用这个问题的答案来创建一个 T4 模板。对 T4 有兴趣的人可能会对这个解决方案感兴趣。 - makerofthings7
1个回答

2

问:对于这个类来说,“DataContext”是一个合适的名称吗?

在.NET中,我们有许多DataContext类,因此从你想要名称适当地传达类所做的事情的意义上讲,我认为XyzQueueDataContext可以适当地传达类所做的事情 - 尽管您无法从中查询。

如果您想保持更符合接受的模式语言企业应用架构模式将封装对外部系统访问的任何类称为网关,而更具体地说,您可能希望在企业集成模式的语言中使用术语通道 - 那是我会这么做的。

问:按照我所做的方式命名队列操作名称是否是一种不好的实践?

嗯,它肯定将队列名称与类紧密耦合。这意味着如果您稍后决定要解耦它们,则无法解耦。

总的来说,我认为这个类可能会受益于尝试做更少的事情。使用队列并不意味着管理它,因此建议将所有队列管理代码放在那里的做法,我建议注入一个CloudQueue到实例中。以下是如何实现AzureChannel构造函数:

private readonly CloudQueue queue;

public AzureChannel(CloudQueue queue)
{
    if (queue == null)
    {
        throw new ArgumentNullException("queue");
    }

    this.queue = queue;
}

这更符合单一职责原则,现在您可以在自己的(可重用)类中实现队列管理。


谢谢!我想我的Blob类也应该被命名为“channel”,对吗?Azure Table呢?因为我可以公开一个IQueryable,所以应该叫做xyzDataContext吗? - makerofthings7
想听听您对DRY和项目结构在此处及其链接的问题上的看法。 - makerofthings7
1
那个问题是为什么不应该在控制器中实现应用逻辑的很好的例子。控制器的单一职责应该是处理传入请求并生成Web结果(通常是HTML)。它应该通过委托给一个框架中立的库来完成:通常是领域模型。 - Mark Seemann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接