使用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方法允许你选择导出格式).

这个问题的解决方案提出了它自己的问题,最明显的错误是:

Microsoft.ReportingServices.Diagnostics.Utilities.MissingSessionIdException: The session identifier is missing. A session identifier is required for this operation.

所以我最后回答的两个问题可能对其他人有价值,如何在没有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服务引用提供程序(在撰写本文时,扩展名处于预览中).
输入服务端点的URI,对我来说这是以http:// [SERVERNAME-OR-IP] /ReportServer/ReportExecution2005.asmx?wsdl的形式,并在底部设置您的命名空间.我保留了所有其他默认值,然后点击完成.我不记得我在第一次进行此设置时在该字段中使用的内容,但我希望它在[MyProjectNamespace] .ReportingServices完成时完成.

这会给你你的班级.

enter image description here

同样,这也可以使用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以获取此新请求.通过这种方式,我确保命名空间和围绕此会话标识符的所有其他复杂性完全是服务提供它们开始的方式,所以我尽可能地确信它们将是有效的.

希望这有助于任何人寻找解决方案.我在网上看到了很多关于这个主题的问题,但没有一个我需要的答案/解释.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


在开发中,有时候生成验证码的场景目前还是存在的,本篇演示不依赖第三方组件,生成随机验证码图片。 先添加验证码接口 public interface ICaptcha { /// &lt;summary&gt; /// 生成随机验证码 /// &lt;/summary&gt; /// &lt;para
后端技术 .net code 官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1 上传方式:multipart/form-data 其他参数:Name,Versio
在上一篇文章中,我们比较出单表插入9999行数据,Freesql &gt;&#160;Dapper &gt; EfCore。在本文中,我们来看看级联插入 构建9999行数据 List&lt;Entity&gt; datas = new List&lt;Entity&gt;(); for (int i
需求:导入9999行数据时Dapper, Ef core, Freesql&#160;谁的性能更优,是如何执行的,级联增加谁性能更佳。 确认方法:sql server&#160;的 sys.dm_exec_query_stats SELECT TOP 1000 (select [text] from
资料整理 1.sp-api介绍:https://developer.amazonservices.com/ 2.github文档:https://github.com/amzn/selling-partner-api-docs 3.github代码:https://github.com/amzn/s
最近时间在整SM2算法,在网上看到不少代码,基本都是使用BouncyCastle库,现在这个版本算比较好的拿来分享给大家。 首先引入包&#160;Portable.BouncyCastle 完整代码见Gitee:https://gitee.com/Karl_Albright/CryptoHelper
在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发、订阅和处理的流程。这种实现太简单了,百十行代码就展示了一个基本工作原理。然而,要将这样的解决方案运用到实际生产环境,还有很长的路要走。今天,我们就研究一下在事件处理器中,对象生命周期的管理问题。事实上,不仅仅是在事件处理器
上文已经介绍了Identity Service的实现过程。今天我们继续,实现一个简单的Weather API和一个基于Ocelot的API网关。 回顾 《Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(一)》 Weather API Weather
最近我为我自己的应用开发框架Apworks设计了一套案例应用程序,并以Apache 2.0开源,开源地址是:https://github.com/daxnet/apworks-examples,目的是为了让大家更为方便地学习和使用.NET Core、最新的前端开发框架Angular,以及Apwork
HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务端接口的耦合度。很多当今流行的RESTful API开发框架,包括Spring REST,也都默认支
在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅、通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现。接下来对于事件驱动型架构的讨论,就需要结合一个实际的架构案例来进行分析。在领域驱动设计的讨论范畴,CQRS架构本身就是事件驱动的,因此,
HAL,全称为Hypertext Application Language,它是一种简单的数据格式,它能以一种简单、统一的形式,在API中引入超链接特性,使得API的可发现性(discoverable)更强,并具有自描述的特点。使用了HAL的API会更容易地被第三方开源库所调用,并且使用起来也很方便
何时使用领域驱动设计?其实当你的应用程序架构设计是面向业务的时候,你已经开始使用领域驱动设计了。领域驱动设计既不是架构风格(Architecture Style),也不是架构模式(Architecture Pattern),它也不是一种软件开发方法论,所以,是否应该使用领域驱动设计,以及什么时候使用
《在ASP.NET Core中使用Apworks快速开发数据服务》一文中,我介绍了如何使用Apworks框架的数据服务来快速构建用于查询和管理数据模型的RESTful API,通过该文的介绍,你会看到,使用Apworks框架开发数据服务是何等简单快捷,提供的功能也非常多,比如对Hypermedia的
在上一讲中,我们已经完成了一个完整的案例,在这个案例中,我们可以通过Angular单页面应用(SPA)进行登录,然后通过后端的Ocelot API网关整合IdentityServer4完成身份认证。在本讲中,我们会讨论在当前这种架构的应用程序中,如何完成用户授权。 回顾 《Angular SPA基于
Keycloak是一个功能强大的开源身份和访问管理系统,提供了一整套解决方案,包括用户认证、单点登录(SSO)、身份联合、用户注册、用户管理、角色映射、多因素认证和访问控制等。它广泛应用于企业和云服务,可以简化和统一不同应用程序和服务的安全管理,支持自托管或云部署,适用于需要安全、灵活且易于扩展的用
3月7日,微软发布了Visual Studio 2017 RTM,与之一起发布的还有.NET Core Runtime 1.1.0以及.NET Core SDK 1.0.0,尽管这些并不是最新版,但也已经从preview版本升级到了正式版。所以,在安装Visual Studio 2017时如果启用了
在上文中,我介绍了如何在Ocelot中使用自定义的中间件来修改下游服务的response body。今天,我们再扩展一下设计,让我们自己设计的中间件变得更为通用,使其能够应用在不同的Route上。比如,我们可以设计一个通用的替换response body的中间件,然后将其应用在多个Route上。 O
不少关注我博客的朋友都知道我在2009年左右开发过一个名为Apworks的企业级应用程序开发框架,旨在为分布式企业系统软件开发提供面向领域驱动(DDD)的框架级别的解决方案,并对多种系统架构风格提供支持。这个框架的开发和维护我坚持了很久,一直到2015年,我都一直在不停地重构这个项目。目前这个项目在
好吧,这个题目我也想了很久,不知道如何用最简单的几个字来概括这篇文章,原本打算取名《Angular单页面应用基于Ocelot API网关与IdentityServer4ʺSP.NET Identity实现身份认证与授权》,然而如你所见,这样的名字实在是太长了。所以,我不得不缩写“单页面应用”几个字