使用Reporting Services(SSRS)作为ASP.NET Core站点中的引用
我努力为此寻找解决方案好几天,所以我想分享我的情况.我正在将现有的ASP.NET MVC应用程序转换为ASP.NET Core MVC.然而,使用ASP.NET Core的最大变化是System.Web命名空间是不行的.但通常情况下,SQL Server Reporting Services(SSRS)通常作为WebReference添加到项目中,该项目基于 – 您猜对了,System.Web.
因此,我需要找到一种能够命中SSRS端点以执行报告的替代方法.对于我的场景,我主要想要PDF(虽然调用SSRS的Render方法允许你选择导出格式). 这个问题的解决方案提出了它自己的问题,最明显的错误是:
所以我最后回答的两个问题可能对其他人有价值,如何在没有System.Web的情况下使用SSRS,以及如何解决“缺少会话标识符”的错误 解决方法
我用来解决这个问题的第一步是Visual Studio的连接服务和WCF.此方法生成一些类似于WebReferences但基于System.DataModel而不是System.Web的类.我应该注意,如果出于任何原因没有Visual Studio,您可以使用SvcUtil.exe工具生成这些相同的类.
当使用VS2017< 15.5时,您需要获取扩展以便从Visual Studio Marketplace添加WCF服务引用.对于VS2017> = 15.5,它现在是built in.之后,当您右键单击Connected Service时,您应该有一个新条目,有用地调用添加连接服务….下一个屏幕上的一个条目应该是Microsoft WCF Web服务引用提供程序(在撰写本文时,扩展名处于预览中). 这会给你你的班级. 同样,这也可以使用SvcUtil.exe来完成. 除了我的新参考之外,我使用的代码/类如下.我尽力使代码尽可能全面.我的实际实现更加重构,但它只是增加了解这一切是如何工作所不需要的复杂性.所以我试图使这段代码尽可能线性化.如果我的代码有错误,请随时告诉我:-) public async Task<byte[]> RenderReport(string report,IDictionary<string,object> parameters,string exportFormat = null) { //My binding setup,since ASP.NET Core apps don't use a web.config file var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm; binding.MaxReceivedMessageSize = 10485760; //I wanted a 10MB size limit on response to allow for larger PDFs //Create the execution service SOAP Client var rsExec = new ReportExecutionServiceSoapClient(binding,new EndpointAddress(reportingServicesUrl)); //Setup access credentials. I use windows credentials,yours may differ var clientCredentials = new NetworkCredential(reportingServicesUserName,reportingServicesPassword,reportingServicesDomain); rsExec.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation; rsExec.ClientCredentials.Windows.ClientCredential = clientCredentials; //This handles the problem of "Missing session identifier" rsExec.Endpoint.Behaviors.Add(new ReportingServicesEndpointBehavior()); //Load the report var taskLoadReport = await rsExec.LoadReportAsync(report,null); //Set the parameteres asked for by the report var reportParameters = taskLoadReport.Parameters.Where(x => parameters.ContainsKey(x.Name)).Select(x => new ParameterValue() { Name = x.Name,Value = parameters[x.Name].ToString() }).ToArray(); await rsExec.SetExecutionParametersAsync(reportParameters,"en-us"); //run the report const string deviceInfo = @"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>"; var response = await rsExec.RenderAsync(new RenderRequest(exportFormat ?? "PDF",deviceInfo)); //spit out the result return response.Result; } 大多数这是自我解释,但我想调出我正在添加的端点行为.请参阅,在加载报告详细信息并随后使用该信息使用我在参数… well参数中设置的值来设置报告的参数,然后呈现报告时,您需要设置会话标识符以连接调用是同一会话上下文的所有部分.它查找的会话标识符是一个名为ExecutionHeader的SOAP标头值,其值为“ExecutionID”.这是对我对LoadReportAsync的调用的响应提供的,但不会自动转移到以后对API的所有调用.我已经尝试了多种方法来实现这一点,但由于固有的类试图将XML命名空间设置为我想要的以外的东西而遇到了问题.最终,EndpointBehavior是最不具侵入性的解决方案(也是唯一一个我工作的解决方案).支持它的类看起来像这样. using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; internal class ReportingServicesEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint,BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint,ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(new ReportingServicesExecutionInspector()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint,EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } } internal class ReportingServicesExecutionInspector : IClientMessageInspector { private MessageHeaders headers; public void AfterReceiveReply(ref Message reply,object correlationState) { var index = reply.Headers.FindHeader("ExecutionHeader","http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices"); if (index >= 0 && headers == null) { headers = new MessageHeaders(MessageVersion.Soap11); headers.CopyHeaderFrom(reply,reply.Headers.FindHeader("ExecutionHeader","http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices")); } } public object BeforeSendRequest(ref Message request,IClientChannel channel) { if(headers != null) request.Headers.CopyHeadersFrom(headers); return Guid.NewGuid(); //https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector.beforesendrequest(v=vs.110).aspx#Anchor_0 } } 这里有两节课;一个是EndpointBehavior,另一个是MessageInspector. EndpointBehavior的唯一目的是连接MessageInspector.我无法找到额外的步骤.但MessageInspector所做的是,每当响应返回时,如果我们还没有从过去的响应中保存ExecutionHeader,我们会从响应中保存一个.随后,每次我们发送请求时,如果我们从过去的响应中保存了ExecutionHeader,我会将其附加到Headers以获取此新请求.通过这种方式,我确保命名空间和围绕此会话标识符的所有其他复杂性完全是服务提供它们开始的方式,所以我尽可能地确信它们将是有效的. 希望这有助于任何人寻找解决方案.我在网上看到了很多关于这个主题的问题,但没有一个我需要的答案/解释. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 没有aspx文件扩展名的ASP.NET网页
- ASP.NET web.config文件是否失控?
- asp.net-mvc-3 – 将输入值传递给Action(ASP.Net MVC 3)
- asp.net-mvc – 使用Nuget打包源代码
- asp.net-mvc – MVC控制器与开箱即用的Sitecore控制器
- asp.net – 对于在Azure部署的Web.config中存储密码的正确程
- asp.net – 如何从列表框中获取所选项目valus而不使用for循
- asp.net-mvc – 在每个网址的末尾添加尾部斜杠?
- asp.net – CultureInfo.CurrentCulture从中读取文化
- iis-7.5 – .NET MVC 3动作是否应该触发w3wp.exe以生成进程
- asp.net-mvc – 在mvc 5和web api 2之间共享owin
- asp.net – 在Sitecore中检索URL路径部分的方法是
- asp.net-mvc – 如何在asp.net mvc中使用jquery设
- asp.net-mvc – 同一页面上的ASP.NET MVC模型绑定
- 将配置文件appsetting中的值转换为动态对象调用
- asp.net-mvc – 为什么Partial View在MVC 5 Visu
- 开始使用asp.net / c #web apps
- asp.net-web-api – 与web api和web api 2之间的
- asp.net-mvc – 在.Net MVC WS-Federation站点中
- asp.net 4.0:是否有相当于ClientIDMode的INPUT的