简体   繁体   English

.NET 客户端 WCF 与排队请求

[英].NET client-side WCF with queued requests

Background背景

I'm working on updating legacy software library.我正在更新旧版软件库。 The legacy code uses an infinitely looping System.Threading.Thread that executes processes in the queue.遗留代码使用无限循环 System.Threading.Thread 执行队列中的进程。 These processes perform multiple requests with another legacy system that can only process one request at a time.这些进程对另一个只能处理一个请求的遗留系统执行多个请求。

I'm trying to modernize, but I'm new to WCF services and there may be a big hole in my knowledge that'd simplify things.我正在尝试现代化,但我是 WCF 服务的新手,我的知识中可能存在一个可以简化事情的大漏洞。

WCF Client-Side Host WCF 客户端主机

In modernizing, I'm trying to move to a client-side WCF service.在现代化过程中,我正在尝试迁移到客户端 WCF 服务。 The WCF service allows requests to be queued from multiple a applications. WCF 服务允许对来自多个应用程序的请求进行排队。 The service takes a request and returns a GUID back so that I can properly associate via the callbacks.该服务接受一个请求并返回一个 GUID,以便我可以通过回调正确关联。

public class SomeService : ISomeService
{
    public Guid AddToQueue(Request request)
    {
    // Code to add the request to a queue, return a Guid, etc.
    }
}

public interface ISomeCallback
{
    void NotifyExecuting(Guid guid)
    void NotifyComplete(Guid guid)
    void NotifyFault(Guid guid, byte[] data)
}

WCF Client Process Queues WCF 客户端进程队列

The problem I'm having is that the legacy processes can include more than one request.我遇到的问题是遗留流程可以包含多个请求。 Process 1 might do Request X then Request Y, and based on those results follow up with Request Z. With the legacy system, there might be Processes 1-10 queued up.进程 1 可能会执行请求 X,然后执行请求 Y,并根据这些结果跟进请求 Z。对于遗留系统,可能会有进程 1-10 排队。

I have a cludgy model where the process is executed.我有一个笨拙的 model 执行该过程。 I'm handling events on the process to know when it's finished or fails.我正在处理进程中的事件以了解它何时完成或失败。 But, it just feels really cludgy...但是,感觉真的很坑爹……

public class ActionsQueue
{
    public IList<Action> PendingActions { get; private set; }
    public Action CurrentAction { get; private set; }

    public void Add(Action action)
    {
        PendingAction.Add(action)
        if (CurrentAction is null)
            ExecuteNextAction();
    }

    private void ExecuteNextAction()
    {
        if (PendingActions.Count > 0)
        {
            CurrentAction = PendingActions[0];
            PendingActions.RemoveAt(0);
            CurrentAction.Completed += OnActionCompleted;
            CurrentAction.Execute();
        }
    }

    private OnActionCompleted(object sender, EventArgs e)
    {
        CurrentAction = default;
        ExecuteNextAction();
    }
}

public class Action
{
    internal void Execute()
    {
    // Instantiate the first request
    // Add handlers to the first request
    // Send it to the service
    }

    internal void OnRequestXComplete()
    {
    // Use the data that's come back from the request
    // Proceed with future requests
    }
}

With the client-side callback the GUID is matched up to the original request, and it raises a related event on the original requests.通过客户端回调,GUID 与原始请求相匹配,并在原始请求上引发相关事件。 Again, the implementation here feels really cludgy.同样,这里的实现感觉非常笨拙。

I've seen example of Async methods for the host, having a Task returned, and then using an await on the Task.我已经看到主机的异步方法示例,返回一个任务,然后在任务上使用等待。 But, I've also seen recommendations not to do this.但是,我也看到了不要这样做的建议。

Any recommendations on how to untangle this mess into something more usable are appreciated.任何关于如何将这种混乱解开成更有用的东西的建议都值得赞赏。 Again, it's possible that there's a hole in my knowledge here that's keeping me from a better solutiong.同样,我的知识可能存在漏洞,这使我无法获得更好的解决方案。

Thanks谢谢

Queued communication between the client and the server of WCF is usually possible using a NetMsmqbinding, which ensures persistent communication between the client and the server. WCF 的客户端和服务器之间的队列通信通常可以使用 NetMsmqbinding 来确保客户端和服务器之间的持久通信。 See this article for specific examples.有关具体示例,请参阅本文

If you need efficient and fast message processing, use a non-transactional queue and set the ExactlyOnce attribute to False, but this has a security impact.如果您需要高效快速的消息处理,请使用非事务性队列并将 ExactlyOnce 属性设置为 False,但这会影响安全。 Check this docs for further info.检查此文档以获取更多信息。

In case anyone comes along later with a similar issue, this is a rough sketch of what I ended up with:万一有人后来遇到类似的问题,这是我最终得到的粗略草图:

[ServiceContract(Name="MyService", SessionMode=Session.Required]
public interface IMyServiceContract
{
    [OperationContract()]
    Task<string> ExecuteRequestAsync(Action action);
}

public class MyService: IMyServiceContract
{
    private TaskQueue queue = new TaskQueue();
    public async Task<string> ExecuteRequestAsync(Request request)
    {
        return await queue.Enqueue(() => request.Execute());
    }
}

public class TaskQueue
{
    private SemaphoreSlim semaphore;
public TaskQueue()
    {
        semaphore = new SemaphoreSlim(1);
    }

    Task<T> Enqueue<T>(Func<T> function)
    {
        await semaphore.WaitAsync();
        try
        {
            return await Task.Factory.StartNew(() => function.invoke();)
        }
        finally
    {
            semaphore.Release();
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM