C# 在模拟时调用异步WCF服务

C# 在模拟时调用异步WCF服务,c#,wcf,asynchronous,impersonation,C#,Wcf,Asynchronous,Impersonation,我在服务器上运行了一个WCF服务,该服务器配置为接受Kerberos身份验证 Kerberos工作正常,因此WCF服务知道哪个用户正在连接他。 该服务以异步方法提供一切。像这里这样(只是clearity的一个例子) publicExampleService:IEExampleService{ 公共任务GetUsernameAsync(){ 返回wait Task.Run(()=>System.Threading.Thread.CurrentPrincipal.Name); } } 在客户端,我

我在服务器上运行了一个WCF服务,该服务器配置为接受Kerberos身份验证

Kerberos工作正常,因此WCF服务知道哪个用户正在连接他。 该服务以异步方法提供一切。像这里这样(只是clearity的一个例子)

publicExampleService:IEExampleService{
公共任务GetUsernameAsync(){
返回wait Task.Run(()=>System.Threading.Thread.CurrentPrincipal.Name);
}
}
在客户端,我有一个控制器(这是一个MVC页面,但这并不重要),它异步调用这些方法

public ExampleController {
    public async Task<ActionResult> Index() {
        using(var serviceClient = ServiceFactory.GetServiceClient())
        using(Security.Impersonation.Impersonate())
        {
            var data = await serviceClient.GetUsernameAsync();
            return View(data);
        }
    }
}
公共示例控制器{
公共异步任务索引(){
使用(var serviceClient=ServiceFactory.GetServiceClient())
使用(Security.Impersonation.Impersonate())
{
var data=await serviceClient.getUsernameAync();
返回视图(数据);
}
}
}
只要我不使用wait,模拟效果就很好

由于
Task
不流动模拟的身份,我想知道是否有可能,改变
Task
的执行用户,或者做任何其他事情,使模拟在这个用例中工作


我尝试了一个定制的等待器(在这种情况下可以使用文化),但这根本不起作用(它也不模拟)。

因为我负责WCF接口,所以这里有一个解决方案可以工作(但我不喜欢,因为它或多或少是代码重复):

[服务合同]
接口IExampleService{
[经营合同]
字符串GetUsername();
}
接口IExampleServiceAsync{
任务GetUserNameAsync();
}
类ExampleService:IEExampleService{
公共字符串GetUsername(){
返回System.Threading.Thread.CurrentPrincipal.Name;
}
}
类ExpampleServiceClient:ServiceClient,IExampleServiceAsync{
公共任务GetUsernameAsync(){
返回任务。运行(()=>GetUsername());
}
私有字符串GetUsername(){
使用(Security.Impersonation.Impersonate())
{
返回base.Proxy.GetUsername();
}
}
}
我不得不说,这是一种变通方法,而不是解决方案,它改变了服务器端的接口(仅限于非异步接口),但至少它是可行的


此解决方案还有一个优点-您可以在ExampleServiceClient上实现模拟作为行为模式。

好的-经过更深入的研究,我终于找到了如何在异步任务中传递模拟windows身份的解决方案

该解决方案是机器范围的,将为所有(在本例中)64位ASP.NET 4.5应用程序设置

C:\Windows\Microsoft.Net\Framework64\v4.0.30319
中找到
aspnet.config
文件(这可能也适用于更高版本),并将
legacyImpersonationPolicy
的值更改为false

<legacyImpersonationPolicy enabled="false"/>
这里解释了aspnet.config设置(顺便说一句,在web.config文件中设置它是不起作用的)(它基本上说,如果这是真的,我们就用.NET 1.1的方法来做)

您可以使用此方法检查windows标识是否流动:

System.Security.SecurityContext.IsWindowsIdentityFlowSuppressed()

我不同意你的问题

问题不在于您的
等待
。但是你的
任务。运行
。实际上不应该有
等待任务。在ASP.Net代码上运行
。它的效果是不必要的线程切换。由于ASP.Net上没有STA线程,因此不需要这样做,这只会降低代码的速度

如果您坚持使用真正的无线程
任务
s,您应该不会有任何问题,因为您将停留在单个线程中。除非您的应用程序服务器的客户机数量非常有限,并且CPU占用的操作数量巨大,否则多线程不利于扩展,因为单个用户可以快速填充服务器的时间表

您应该真正使用
Task.FromResult
TaskCompletionSource.Task
来确保保持单线程。顺便说一句,这将解决您的
[ThreadLocal]
属性问题

TL:DR

不要使用
任务。在服务器端运行
。使用
Task.FromResult
,这样您只有一个线程

编辑:响应

哪根线?在客户端,您仍将使用
wait
。我从来没有说过不要使用
等待
。我说过不要直接在任务中使用
wait
。运行
(UI线程除外)。我没说你应该阻止线程。因为你的线程应该做一些工作来产生传递到
任务中的结果。FromResult
。阻塞意味着线程什么也不做,同时消耗资源(即内存)。见鬼,根本没必要这么做

服务器端应使用以下模式:

public ExampleService : IExampleService 
{
    public Task<string> GetUsernameAsync() 
    {
       var name = System.Threading.Thread.CurrentPrincipal.Name;
       return Task.FromResult(name);
    }
}
publicExampleService:IEExampleService
{
公共任务GetUsernameAsync()
{
var name=System.Threading.Thread.CurrentPrincipal.name;
返回Task.FromResult(名称);
}
}
客户应该留下来

public ExampleController 
{
    public async Task<ActionResult> Index() 
    {
        using(var serviceClient = ServiceFactory.GetServiceClient())
        using(Security.Impersonation.Impersonate())
        {
            var data = await serviceClient.GetUsernameAsync();
            return View(data);
        }
    }
}
公共示例控制器
{

异步的公共异步任务样式。
Task.Run
是异步的并发样式,仅当您需要使用另一个线程时才应使用(因为您是CPU绑定的,或者此线程需要用于其他用途).

这意味着我宁愿阻塞线程长达5秒,而不是使用async和Wait?不。不应该发生任何阻塞。但同时,不应该不必要地发生线程切换。你应该阅读啊,现在我明白你的意思了,但是任务。在服务器端运行只是一个简单的解释。但你可能在这一点上是对的但我的问题不是关于服务器端-模拟在客户端的
wait
上失败(或未流动)。
System.Security.SecurityContext.IsWindowsIdentityFlowSuppressed()
public ExampleService : IExampleService 
{
    public Task<string> GetUsernameAsync() 
    {
       var name = System.Threading.Thread.CurrentPrincipal.Name;
       return Task.FromResult(name);
    }
}
public ExampleController 
{
    public async Task<ActionResult> Index() 
    {
        using(var serviceClient = ServiceFactory.GetServiceClient())
        using(Security.Impersonation.Impersonate())
        {
            var data = await serviceClient.GetUsernameAsync();
            return View(data);
        }
    }
}