Get Data from SSRS dataSet - reporting-services

Is it possible (if Yes - how) to retrieve data from SSRS dataset that has been published to the ReportServer ?
I have is Azure reporting services setup, and published a DataSource, DataSet, and a report that work perfectly fine.
What I want - is to be able to access that "published DataSet" - like some sort of a XML API ?
Say hit some URL with params from a browser, and get a XML result with Data from that DataSet

The problem you will run into is the rsd (datasetFile) is not much itself but a query and for that reason publishing the proxy services of ReportService2010 will get you the data but you still have to then handle the XML shredding. It is much, much easier to get the data from the dataset through querying the 'ReportServer' IMHO in SQL directly. Rather than making the models from the proxy classes, getting the data from invoking those classes, then you have xml you still have to query to get your data.
Example with word of warning: (This may only work with my example of datasets not other catalog items):
use ReportServer
GO
Select
Path
, Name
, Content
, cast( cast(Content as varbinary(max)) as xml) as ContentAsXML
, cast(
cast(Content as varbinary(max))
as xml)
.query('declare namespace a="http://schemas.microsoft.com/sqlserver/reporting/2010/01/shareddatasetdefinition";
/a:SharedDataSet/a:DataSet/a:Query/a:CommandText')
.value('.', 'varchar(max)') as QueryStatement
from dbo.Catalog
where type = 8
Based on this guy's writing:
http://bretstateham.com/extracting-ssrs-report-rdl-xml-from-the-reportserver-database/
If you decide you just must use the proxy classes you will have to remember to invoke the namespace and I did an Xdocument method with C# to get the data. Don't have the dataset but I did the Datasource which will be a similar method. You mainly do this method in a foreach loop for each dataset you want. Keep in mind YOUR NAMESPACE for 2008 or 2005 will differ:
private List<string> GetDataSourceRefs(string aSourceLocation)
{
var xdoc = XDocument.Load(aSourceLocation);
// Need a namespace or else the xml elements will not be properly identified. Default below is for 2012 ONLY.
XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition");
return xdoc.Descendants(ns + "DataSource")
.Elements(ns + "DataSourceReference")
.Select(x => x.Value)
.ToList();
}

Use ReportExecution2005 proxy class - you can execute the report and get it in the XML format. I'm not sure but I think you should be able to execute datasets too. You can read more about it here:
Generate reports programmatically using the TFS API and SSRS

Related

SSIS consolidate and concatenate multiple rows into single rows without using SQL

I am trying to accomplish something that is pretty easy to do in SQL, but seemingly very challenging to do in SSIS without using SQL. Basically, I need to consolidate and concatenate a field of a many-to-one relationship.
Given entities: [Contract Item] (many) to (one) [Account]
There is a field [ari_productsummary] that contains the product listed on the Contract Item entity. We want to write that value to the Account as [ari_activecontractitems]. However, an Account may have more than one Contract Item record associated to it, in which case, we want to concatenate those values. We also only want the distinct values to be concatenated (distinct rows already solved within my data flow).
This can be accomplished by writing to a temporary table, and then using a query or view to obtain the summarized results as followed. I created a SQL table called TESTTABLE that contains the [ari_productsummary] from the Contract Item entity along with the referring [accountid] to map it back to Account. I then wrote the following query as a view:
SELECT distinct accountid,
(SELECT TT2.ari_productsummary + '; '
FROM TESTTABLE TT2
WHERE TT2.accountid = TT.accountid
FOR XML PATH ('')
) AS 'ari_activecontractitems'
FROM TESTTABLE TT
Executing that Query provides me the results that I want, which I can then use for importing into the Account entity as shown below:
But how do I do this in a SSIS dataflow without writing to a SQL table as a temporary placeholder for the data?? I want to do the entire process inside one dataflow container, without using a temporary SQL table/view. The whole summarization process needs to be done on the fly:
Does anyone have a solution that doesn't require a temporary SQL table/view/query, but is contained entirely within the data flow?
I am using VS 2017 and the KingswaySoft Dynamic CRM 365 ETL toolset to develop my solution/package.
Spit balling here as I don't Dynamics nor do I have the custom components.
Data Flow 1 - Contract aggregation
The purpose of this data flow is to replicate your logic in the elegant query you provided and shove that into a Cache Connection Manager (see Notes for 2008+ at the end)
KingswaySoft Dynamics Source -> Script Task -> Cache Transform
If you want to keep the sort in there, do it before the script task. The implementation I'll take with the Script Task is that it's fully blocking - that is all the rows must arrive before it can send any on. Tasks like the Merge Join are only partially blocking because the requirement of sorted data means that once you no longer have a match for the current item, you can send it on down the pipeline.
The Script Task is going to be asynchronous transformation. You'll have two output columns, your key accountid and your new derived column of ari_activecontractitems. That column will might need to be big - you'll know your data best but if it's a blob type in Dynamics (> 4k unicode or > 8k ascii characters) then you'll have to define the data type as DT_TEXT/DT_NTEXT
As inputs, you'll select accountid and ari_productsummary from your source.
The code should be pretty easy. We're going to accumulate the inbound data into a Dictionary.
// member variable
Dictionary<string, List<string>> accumulator;
The PreProcess method, we'll tack this in there to initialize our variable
// initialize in PreProcess method
accumulator = new Dictionary<string, List<string>>();
In the OnBufferRowSent (name approx)
// simulate the inbound queue
// row_id would be something like Rows.row_id
if (!accumulator.ContainsKey(row_id))
{
// Create an empty dictionary for our list
accumulator.Add(row_id, new List<string>());
}
// add it if we don't have it
if (!accumulator[row_id].Contains(invoice))
{
accumulator[row_id].Add(invoice);
}
Once you get the signal sent of no more data available, that's when you start buffering output data. The auto generated code will have placeholders for all this.
// This is how we shove data out the pipe
foreach(var kvp in accumulator)
{
// approximately thus
OutputBuffer1.AddRow();
OutputBuffer1.row_id = kvp.Key;
OutputBuffer1.ari_productsummary = string.Join("; ", kvp.Value);
}
We have an upcoming release that comes with a component that does exactly what you are trying to achieve without the need of writing custom code. The feature is currently under preview, please reach out to us for private access to the feature. You can find our contact information on our website.
UPDATE - June 5, 2020, we have made the components available for public access at https://www.kingswaysoft.com/products/ssis-productivity-pack/ as a result of our 2020 Release Wave 1. We have two components available that serve this kind of purpose. The Composition component will take input values and transform into a composite value in a SSIS column. The Decomposition component does the opposite, it would take an input value and split it into multiple rows using either delimiter-based text splitting or XML/JSON array splitting.

C# or BIML code for inserting records into db

I want to insert values into database when the biml code is ran and the package has completed expansion is this possible using BIML or c#?
I have a table called BIML expansion created in my DB and I have test.biml which loads the package test.dtsx whenever the BIML expansion is completed a record should be inserted into my table that expansion has been completed.
Let me know if you have any questions or needs any additional info.
From comments
I tried your code
string connectionString = "Data Source=hq-dev-sqldw01;Initial Catalog=IM_Stage;Integrated Security=SSPI;Provider=SQLNCLI11.1";
string SrcTablequery=#"INSERT INTO BIML_audit (audit_id,Package,audit_Logtime) VALUES (#audit_id, #Package,#audit_Logtime)";
DataTable dt = ExternalDataAccess.GetDataTable(connectionString,SrcTablequery);
It has an error below must declare the scalar variable audit_id can you let me know the issue behind it?
In it's simplest form, you'd have content like this in your Biml script
// Define the connection string to our database
string connectionStringSource = #"Server=localhost\dev2012;Initial Catalog=AdventureWorksDW2012;Integrated Security=SSPI;Provider=SQLNCLI11.1";
// Define the query to be run after *ish* expansion
string SrcTableQuery = #"INSERT INTO dbo.MyTable (BuildDate) SELECT GETDATE()";
// Run our query, nothing populates the data table
DataTable dt = ExternalDataAccess.GetDataTable(connectionStringSource, SrcTableQuery);
Plenty of different ways to do this - you could have spun up your own OLE/ADO connection manager and used the class methods. You could have pulled the connection string from the Biml Connections collection (depending on the tier this is executed in), etc.
Caveats
Depending on the product (BimlStudio vs BimlExpress), there may be a background process compiling your BimlScript to ensure all the metadata is ready for intellisense to pick it up. You might need to stash that logic into a very high tiered Biml file to ensure it's only called when you're ready for it. e.g.
<## template tier="999" #>
<#
// Define the connection string to our database
string connectionStringSource = #"Server=localhost\dev2012;Initial Catalog=AdventureWorksDW2012;Integrated Security=SSPI;Provider=SQLNCLI11.1";
// Define the query to be run after *ish* expansion
string SrcTableQuery = #"INSERT INTO dbo.MyTable (BuildDate) SELECT GETDATE()";
// Run our query, nothing populates the data table
DataTable dt = ExternalDataAccess.GetDataTable(connectionStringSource, SrcTableQuery);
#>
Is that the problem you're trying to solve?
Addressing comment/questions
Given the query of
string SrcTablequery=#"INSERT INTO BIML_audit (audit_id,Package,audit_Logtime) VALUES (#audit_id, #Package,#audit_Logtime)";
it errors out due to #audit_id not being specified. Which makes sense - this query specifies it will provide three variables and none are provided.
Option 1 - the lazy way
The quickest resolution would be to redefine your query in a manner like this
string SrcTablequery=string.Format(#"INSERT INTO BIML_audit (audit_id,Package,audit_Logtime) VALUES ({0}, '{1}', '{2})'", 123, "MyPackageName", DateTime.Now);
I use the string library's Format method to inject the actual values into the placeholders. I assume that audit_id is a number and the other two are strings thus the tick marks surrounding 1 and 2 there. You'd need to define a value for your audit id but I stubbed in 123 as an example. If I were generating packages, I'd likely have a variable for my packageName so I'd reference that in my statement as well.
Option 2 - the better way
Replace the third line with .NET library usage much as you see in heikofritz on using parameters inserting data into access database.
1) Create a database Connection
2) Open connection
3) Create a command object and associate with the connection
4) Specify your statement (use ? as your ordinal marker instead of named parameters since this is oledb)
5) Create an Parameter list and associate with values
Many, many examples out there beyond the referenced but it was the first hit. Just ignore the Access connection string and use your original value.

WebAPI Entity Framework DB first with Web API OData controller: Issue with Model properties aliasing

I have been facing an issue with Web API 2.2 with Web API OData controller. I am doing a PoC in which I need to display the JSON output from my service with different column names than that of corresponding Model props (DB table columns).
(For ex: 'CompanyName' from Customer table should appear as 'cName' in JSON output of my service.)
I am using DB first approach with Northwind database, created a Model with ADO.NET EF and created a controller using OData EF. (all default no code changes so far)
Now, I have tried to get different names using
1) Data Contract and Data Member -> specifying directly on Model class (yes, auto generated one)
2) JsonProperty -> specifying directly on Model class (yes, auto generated one)
3) DTOs [it works but I don't want use DTOs]
Unfortunately, first 2 approaches are not working for me (not sure what I'm missing here) and DTOs I'm trying to avoid.
I'm stuck on this all my day today, appreciate if you can point me to a right approach.
Note: Instead of OData controller if I use regular Web API controller, all works.
I realize this is old, and I'm not sure which version of OData you are using but the simple answer is, you have to specify all of this information in the model builder.
Here's an example.
var builder = new ODataConventionModelBuilder();
var entity = builder.EntitySet<Model>("models").EntityType;
entity.Name = "model";
entity.Property(p => p.Id).Name = "id";
entity.Property(p => p.Name).Name = "name";
entity.Property(p => p.Description).Name = "description";
Good luck!

Easily localized alternative to SQL Server Reporting Services?

We are running into the well-documented problems with localizing our SSRS reports.
Can anyone recommend an alternative? Presume parity (or nearly so) with SSRS' functionality, though a great many of our reports will be simple grids or graphs, with some header/footer text. We want a means by which we can easily identify localizable strings, store them in a database, translate them, and then generate the localized "report definition" at deployment time. The Spanish see Spanish reports, the Italians see Italian reports, etc.
Thanks everyone.
There's a book called "Microsoft SQL Server Reporting Services Recipes" that has a few pages dedicated to how to localize SSRS reports. The only limitation is that parameter prompt text can still only show a single language (as it doesn't allow expressions); if you're accessing reports via a client though then it won't be an issue.
It involves creating a custom assembly in VS that has a main function that does the translation (using localization resource files, in much the same way you'd do it for an application).
Then you override the report's OnInit in custom code to initialise the custom assembly resource manager with the report name (so it knows which set of strings to look up, I suppose), and then instead of using "Name: " text in your report, you can use =Code.my_localizer.GetLocalText("Name")
try to put some parameter on ssrs and create procedure like this:
--TEST
--DECLARE #language int
--SET #language = 2 --1 Italian --2 Spain
--passing language parameter from ssrs report
IF (#language = 1)
SELECT englishNameField1 as italianFieldName1, englishNameField2 as italianFieldName2, englishNameField3 as italianFieldName3
FROM tableName
ELSE
SELECT englishNameField1 as spanishNameField1, englishNameField2 as spanishNameField2, englishNameField3 as spanishNameField3
FROM tableName
WHERE parameterFromSSRS = #language
in report put parameter #language with some expression on UserID (Built-in Fields) to get language(localization)

Return SQL query BOXI R3 Webi using Rebean API

Has anyone figured out how to return the SQL for an object that is in Webi using the Rebean API? I have got the ReportExpression of the column but, from there I am having a difficult time retrieving the SQL of the object.
Thanks
I believe you have gone to far into the structure. To get the SQL that is generated you should go through the DataProvider of the rebean. I have not attempted this to see what values I got, but looking at the API methods the getQuery().getSQL() methods of the DataProvider appear to be what you are looking for.
For better clarity:
DocumentInstance docInst; // get your DocumentInstance object via preferred route
DataProvider dp = docInst.getDataProviders().getItem(0); // retrieve the DataProvider that youw ould like the SQL for
String sql = dp.getQuery().getSQL(); // the SQL out of the DataProvider
getSQL() is deprecated so it may disappear in the next version