SSRS2008: LocalReport export to HTML / fragment - ssrs-2008

I need local RDL report to be exported to HTML, preferably HTML fragment. In 2005 it wasn't officially supported but there was a trick. In SSRS2008 they seem to drop this support (there's no HTML extension in the supported extensions when enumerating using reflection) and use RPL instead which is a binary format that I doubt someone will be happy to parse. Actually it's doesn't seem to be about HTML at all.
Now, is there a way to render HTML using SSRS2008 local report?
Notice that I use VS2008 but with reporting assemblies installed from VS2010 Beta 2 reportviewer.

I found a way, but it's not very good.
Export report to mhtml (it's supported by SSRS2008)
Then use System.Windows.Forms.WebBrowser for render mhtml.
In wb.DocumentText property will be full html page.
It's not very good, because you need a file (as url for WebBrowser).
And also, if I use WebBrowser in ASP.NET application, I need to process it in another thread, with STA ApartmentState.

Looking for the same thing. I suppose I suppose we could try to grab the rendered output of the ReportViewer somehow with reflection?
I might play around with it in a little bit to see what I can come up with.

If you can get the mht, you can extract it's content with MIMER.
There is a nu-get package here (MIMER will require .NET Framework 3.5):
https://www.nuget.org/packages/MIMER/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using MIMER;
namespace MimerTest
{
// https://github.com/smithimage/MIMER/blob/master/MIMERTests/MHT/MhtTests.cs
// https://github.com/smithimage/MIMER/
static class Program
{
/// <summary>
/// Der Haupteinstiegspunkt für die Anwendung.
/// </summary>
[STAThread]
static void Main()
{
if (false)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
System.IO.Stream m_Stream;
string path = #"d:\USERNAME\documents\visual studio 2013\Projects\MimerTest\MimerTest\whatismht.mht";
System.IO.FileInfo finf = new System.IO.FileInfo(path);
m_Stream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
var reader = new MIMER.RFC2045.MailReader();
MIMER.IEndCriteriaStrategy endofmessage = new MIMER.RFC2045.BasicEndOfMessageStrategy();
var message = reader.ReadMimeMessage(ref m_Stream, endofmessage);
System.Collections.Generic.IDictionary<string,string> allContents = message.Body;
string strFile = allContents["text/html"];
foreach (System.Collections.Generic.KeyValuePair<string,string> kvp in allContents)
{
System.Console.WriteLine(kvp.Key);
System.Console.WriteLine(kvp.Value);
}
System.Console.WriteLine(" --- Press any key to continue --- ");
System.Console.ReadKey();
}
}
}
In the 2005 ReportViewer, you can enable HTML via reflection:
private static void EnableFormat(ReportViewer viewer, string formatName)
{
const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
FieldInfo m_previewService = viewer.LocalReport.GetType().GetField
(
"m_previewService",
Flags
);
MethodInfo ListRenderingExtensions = m_previewService.FieldType.GetMethod
(
"ListRenderingExtensions",
Flags
);
object previewServiceInstance = m_previewService.GetValue(viewer.LocalReport);
IList extensions = ListRenderingExtensions.Invoke(previewServiceInstance, null) as IList;
PropertyInfo name = extensions[0].GetType().GetProperty("Name", Flags);
foreach (object extension in extensions)
{
if (string.Compare(name.GetValue(extension, null).ToString(), formatName, true) == 0)
{
FieldInfo m_isVisible = extension.GetType().GetField("m_isVisible", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo m_isExposedExternally = extension.GetType().GetField("m_isExposedExternally", BindingFlags.NonPublic | BindingFlags.Instance);
m_isVisible.SetValue(extension, true);
m_isExposedExternally.SetValue(extension, true);
break;
}
}
}
Usage:
var Viewer = new Microsoft.Reporting.WebForms.ReportViewer();
EnableFormat(Viewer, "HTML4.0");
You might also find this interesting:
http://www.codeproject.com/Articles/23966/Report-Viewer-generate-reports-MS-Word-formats

Related

Is there a way to view COM entries by traversing a TLB file in .NET?

I'm converting an application to use registration free COM. There are a few 3rd party COM dll's that would normally have regsvr32 called on them. I tested that I can create objects from these 3rd party dlls by making a side-by-side manifest.
I used the OLE/COM viewer built into Windows to get this information. However I would like to make a program that could do this for me manually, as these 3rd party libraries have lots of classes I need to put in the manifest.
Does anyone know of a way to programatically traverse a type library?
I took Hans' advice and used LoadTypeLib.
For anyone looking for example code, this should be a great starting point.
I wrote it this morning and was able to get xml that I needed.
Forgive me for not releasing the objects! I don't have time to fully flesh out the rest of this answer right now. Edits are welcome.
[DllImport("oleaut32.dll", PreserveSig = false)]
public static extern ITypeLib LoadTypeLib([In, MarshalAs(UnmanagedType.LPWStr)] string typelib);
public static void ParseTypeLib(string filePath)
{
string fileNameOnly = Path.GetFileNameWithoutExtension(filePath);
ITypeLib typeLib = LoadTypeLib(filePath);
int count = typeLib.GetTypeInfoCount();
IntPtr ipLibAtt = IntPtr.Zero;
typeLib.GetLibAttr(out ipLibAtt);
var typeLibAttr = (System.Runtime.InteropServices.ComTypes.TYPELIBATTR)
Marshal.PtrToStructure(ipLibAtt, typeof(System.Runtime.InteropServices.ComTypes.TYPELIBATTR));
Guid tlbId = typeLibAttr.guid;
for(int i=0; i< count; i++)
{
ITypeInfo typeInfo = null;
typeLib.GetTypeInfo(i, out typeInfo);
//figure out what guids, typekind, and names of the thing we're dealing with
IntPtr ipTypeAttr = IntPtr.Zero;
typeInfo.GetTypeAttr(out ipTypeAttr);
//unmarshal the pointer into a structure into something we can read
var typeattr = (System.Runtime.InteropServices.ComTypes.TYPEATTR)
Marshal.PtrToStructure(ipTypeAttr, typeof(System.Runtime.InteropServices.ComTypes.TYPEATTR));
System.Runtime.InteropServices.ComTypes.TYPEKIND typeKind = typeattr.typekind;
Guid typeId = typeattr.guid;
//get the name of the type
string strName, strDocString, strHelpFile;
int dwHelpContext;
typeLib.GetDocumentation(i, out strName, out strDocString, out dwHelpContext, out strHelpFile);
if (typeKind == System.Runtime.InteropServices.ComTypes.TYPEKIND.TKIND_COCLASS)
{
string xmlComClassFormat = "<comClass clsid=\"{0}\" tlbid=\"{1}\" description=\"{2}\" progid=\"{3}.{4}\"></comClass>";
string comClassXml = String.Format(xmlComClassFormat,
typeId.ToString("B").ToUpper(),
tlbId.ToString("B").ToUpper(),
strDocString,
fileNameOnly, strName
);
//Debug.WriteLine(comClassXml);
}
else if(typeKind == System.Runtime.InteropServices.ComTypes.TYPEKIND.TKIND_INTERFACE)
{
string xmlProxyStubFormat = "<comInterfaceExternalProxyStub name=\"{0}\" iid=\"{1}\" tlbid=\"{2}\" proxyStubClsid32=\"{3}\"></comInterfaceExternalProxyStub>";
string proxyStubXml = String.Format(xmlProxyStubFormat,
strName,
typeId.ToString("B").ToUpper(),
tlbId.ToString("B").ToUpper(),
"{00020424-0000-0000-C000-000000000046}"
);
//Debug.WriteLine(proxyStubXml);
}
}
return;
}
}

rendering reportviewer as PDf affects the display

After performing "serverreport.render" in order to print the report in PDF format, the Interactive sort does not work on the WebPage.
Below is my code.
private void Print()
{
HttpContext cont = HttpContext.Current;
HttpResponse myRes = cont.Response;
System.Web.SessionState.HttpSessionState mySess = cont.Session;
string enCoding = null;
string strNull = null;
string strPDF = "PDF";
enCoding = Request.Headers["Accept-Encoding"];
string mimeType = "application/pdf";
string extension = ".pdf";
string[] streamids = null;
Warning[] warnings = null;
byte[] mybytes = null;
mybytes = ReportViewer1.ServerReport.Render(strPDF, strNull, out mimeType,
out enCoding, out extension, out streamids, out warnings);
myRes.Buffer = true;
myRes.Clear();
myRes.ContentType = "application/pdf";
myRes.AddHeader("Content-disposition", "filename=output.pdf");
myRes.BinaryWrite(mybytes);
myRes.Flush();
myRes.End();
}
Apparently the rendering of the report as PDF messes up things.
All suggestions are welcome.
Thanks
David
If you require interactive sorting to be available on the web interface, you'll need to display the report via the Report Viewer or HTML (but not MHTML).
Once a report has been rendered to anything other than Report Viewer or HTML, Interactive Sorting is not available.
Comparing Interactive Functionality for Different Report Rendering Extensions

Crystal Reports - Search Dependencies

Is there a tool that will let you search a number of different crystal reports to see where a specific Table/View/SP is being used?
Scenario is this: We have over 200 reports, so when making a change to a View or Stored Procedure it is not easy to find which reports will be affected without opening each one and checking the "Database expert" or "Datasource location".
I have tried Agent Ransack on them and it doesn't pick any table or view names up.
I never found a tool to do this, so rolled my own in C# .Net 4.0.
If the crystal report uses a 'SQL Command' instead of dragging in tables, it becomes a bit tricky. I suggest only searching for the TableName rather than the fully qualified Database.dbo.TableName - since this may have been omitted in the pasted SQL Command.
usage:
var reports = CrystalExtensions.FindUsages("C:/Reports", "table_name");
code:
namespace Crystal.Helpers
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CrystalDecisions.CrystalReports.Engine;
using Microsoft.CSharp.RuntimeBinder;
public static class CrystalExtensions
{
public static List<string> FindUsages(string directory, string tableName)
{
var result = new List<string>();
foreach (var file in Directory.EnumerateFiles(directory, "*.rpt", SearchOption.AllDirectories))
{
using (var report = new ReportClass { FileName = file })
{
if (report.Database == null) continue;
var tables = report.Database.Tables.ToList();
var hasTable = tables.Any(x => x.Name == tableName || x.GetCommandText().Contains(tableName));
if (hasTable)
result.Add(file);
}
}
return result;
}
public static List<Table> ToList(this Tables tables)
{
var list = new List<Table>();
var enumerator = tables.GetEnumerator();
while (enumerator.MoveNext())
list.Add((Table)enumerator.Current);
return list;
}
public static string GetCommandText(this Table table)
{
var propertyInfo = table.GetType().GetProperty("RasTable", BindingFlags.NonPublic | BindingFlags.Instance);
try
{
return ((dynamic)propertyInfo.GetValue(table, propertyInfo.GetIndexParameters())).CommandText;
}
catch (RuntimeBinderException)
{
return ""; // for simplicity of code above, really should return null
}
}
}
}
Hope that helps!
See the question here:
Any way to search inside a Crystal Report
Another option is to roll-your-own piece of software to do it, but that might be more time consuming than you're looking for. Or, find someone who already has done this :) If you find something that works, let the rest of us know because we're all in the same boat. Good luck!

how to programmatically access the builtin properties of an open xml worddoc file

i would like to access some built in properties(like author,last modified date,etc.) of an open xml word doc file. i would like to use open xml sdk2.0 for this purpose. so i wonder if there is any class or any way i could programmatically access these builtin properties.
An explanation of the following method can be found here, but pretty much you need to pass in the properties that you want to get out of the core.xml file to this method and it will return the value:
public static string WDRetrieveCoreProperty(string docName, string propertyName)
{
// Given a document name and a core property, retrieve the value of the property.
// Note that because this code uses the SelectSingleNode method,
// the search is case sensitive. That is, looking for "Author" is not
// the same as looking for "author".
const string corePropertiesSchema = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
const string dcPropertiesSchema = "http://purl.org/dc/elements/1.1/";
const string dcTermsPropertiesSchema = "http://purl.org/dc/terms/";
string propertyValue = string.Empty;
using (WordprocessingDocument wdPackage = WordprocessingDocument.Open(docName, true))
{
// Get the core properties part (core.xml).
CoreFilePropertiesPart corePropertiesPart = wdPackage.CoreFilePropertiesPart;
// Manage namespaces to perform XML XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("cp", corePropertiesSchema);
nsManager.AddNamespace("dc", dcPropertiesSchema);
nsManager.AddNamespace("dcterms", dcTermsPropertiesSchema);
// Get the properties from the package.
XmlDocument xdoc = new XmlDocument(nt);
// Load the XML in the part into an XmlDocument instance.
xdoc.Load(corePropertiesPart.GetStream());
string searchString = string.Format("//cp:coreProperties/{0}", propertyName);
XmlNode xNode = xdoc.SelectSingleNode(searchString, nsManager);
if (!(xNode == null))
{
propertyValue = xNode.InnerText;
}
}
return propertyValue;
}
You can also use the packaging API:
using System.IO.Packaging.Package;
[...]
using (var package = Package.Open(path))
{
package.PackageProperties.Creator = Environment.UserName;
package.PackageProperties.LastModifiedBy = Environment.UserName;
}
That works also for other open XML formats like power point.
package.Save();
Then
package.closed;
I think that Is the best way.

Changing schema name on runtime - Entity Framework

I need to change the storage schema of the entities on runtime.
I've followed a wonderful post, available here:
http://blogs.microsoft.co.il/blogs/idof/archive/2008/08/22/change-entity-framework-storage-db-schema-in-runtime.aspx?CommentPosted=true#commentmessage
This works perfectly, but only for queries, not for modifications.
Any idea why?
Well, I was looking for this piece of code all around the Internet. In the end I had to do it myself. It's based on Brandon Haynes adapter, but this function is all you need to change the schema on runtime - and you don't need to replace the autogenerated context constructors.
public static EntityConnection Create(
string schema, string connString, string model)
{
XmlReader[] conceptualReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".csdl")
)
};
XmlReader[] mappingReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".msl")
)
};
var storageReader = XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".ssdl")
);
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl";
var storageXml = XElement.Load(storageReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet"))
{
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null)
{
schemaAttribute.SetValue(schema);
}
}
storageXml.CreateReader();
StoreItemCollection storageCollection =
new StoreItemCollection(
new XmlReader[] { storageXml.CreateReader() }
);
EdmItemCollection conceptualCollection = new EdmItemCollection(conceptualReader);
StorageMappingItemCollection mappingCollection =
new StorageMappingItemCollection(
conceptualCollection, storageCollection, mappingReader
);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories
.GetFactory(connectionData.Provider)
.CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}
The resulting EntityConnection should be passed as a parameter when instantiating the context. You can modify it, so all ssdl models are modified by this function, not only the specified one.
I've managed to resolve this issue by using a brilliant library, located in CodePlex (courtesy of Brandon Haynes), named "Entity Framework Runtime Model Adapter", available here:
http://efmodeladapter.codeplex.com/
I've tweaked it a bit, to fit our needs and without the need of replacing the designer code at all.
So, I'm good.
Thanks anyways, and especially to Brandon, amazing job!
I need import data from postgres database. It by default use schema "public". So I use Entity Framework CTP 4 "Code first". It by default use schema "dbo". To change it in runtime I use:
public class PublicSchemaContext : DbContext
{
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder builder)
{
builder.Entity<series_categories>().MapSingleType().ToTable("[public].[series_categories]");
}
public DbSet<series_categories> series_categories { get; set; }
}
It work for select, insert, update and delete data. So next test in pass:
[Test]
public void AccessToPublicSchema()
{
// Select
var db = new PublicSchemaContext();
var list = db.series_categories.ToList();
Assert.Greater(list.Count, 0);
Assert.IsNotNull(list.First().series_category);
// Delete
foreach (var item in db.series_categories.Where(c => c.series_category == "Test"))
db.series_categories.Remove(item);
db.SaveChanges();
// Insert
db.series_categories.Add(new series_categories { series_category = "Test", series_metacategory_id = 1 });
db.SaveChanges();
// Update
var test = db.series_categories.Single(c => c.series_category == "Test");
test.series_category = "Test2";
db.SaveChanges();
// Delete
foreach (var item in db.series_categories.Where(c => c.series_category == "Test2"))
db.series_categories.Remove(item);
db.SaveChanges();
}
Not an answer per se but a followup on Jan Matousek's Create[EntityConnection] method showing how to use from a DbContext. Note DB is the DbContext type passed to the generic repository.
public TxRepository(bool pUseTracking, string pServer, string pDatabase, string pSchema="dbo")
{
// make our own EF database connection string using server and database names
string lConnectionString = BuildEFConnectionString(pServer, pDatabase);
// do nothing special for dbo as that is the default
if (pSchema == "dbo")
{
// supply dbcontext with our connection string
mDbContext = Activator.CreateInstance(typeof(DB), lConnectionString) as DB;
}
else // change the schema in the edmx file before we use it!
{
// Create an EntityConnection and use that to create an ObjectContext,
// then that to create a DbContext with a different default schema from that specified for the edmx file.
// This allows us to have parallel tables in the database that we can make available using either schema or synonym renames.
var lEntityConnection = CreateEntityConnection(pSchema, lConnectionString, "TxData");
// create regular ObjectContext
ObjectContext lObjectContext = new ObjectContext(lEntityConnection);
// create a DbContext from an existing ObjectContext
mDbContext = Activator.CreateInstance(typeof(DB), lObjectContext, true) as DB;
}
// finish EF setup
SetupAndOpen(pUseTracking);
}
I was able to convert the solution from Jan Matousek to work in vb.net 2013 with entity framework 6. I will also try to explain how to use the code in vb.net.
We have a JD Edwards Database which uses different Schema's for each environment (TESTDTA, CRPDTA, PRODDTA). This makes switching between environments cumbersome as you have to manually modify the .edmx file if you want to change environments.
First step is to create a partial class that allows you to pass a value to the constructor of your entities, by default it uses the values from your config file.
Partial Public Class JDE_Entities
Public Sub New(ByVal myObjectContext As ObjectContext)
MyBase.New(myObjectContext, True)
End Sub
End Class
Next create the function that will modify your store schema .ssdl file in memory.
Public Function CreateObjectContext(ByVal schema As String, ByVal connString As String, ByVal model As String) As ObjectContext
Dim myEntityConnection As EntityConnection = Nothing
Try
Dim conceptualReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".csdl"))
Dim mappingReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".msl"))
Dim storageReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".ssdl"))
Dim storageNS As XNamespace = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl"
Dim storageXml = XDocument.Load(storageReader)
Dim conceptualXml = XDocument.Load(conceptualReader)
Dim mappingXml = XDocument.Load(mappingReader)
For Each myItem As XElement In storageXml.Descendants(storageNS + "EntitySet")
Dim schemaAttribute = myItem.Attributes("Schema").FirstOrDefault
If schemaAttribute IsNot Nothing Then
schemaAttribute.SetValue(schema)
End If
Next
storageXml.Save("storage.ssdl")
conceptualXml.Save("storage.csdl")
mappingXml.Save("storage.msl")
Dim storageCollection As StoreItemCollection = New StoreItemCollection("storage.ssdl")
Dim conceptualCollection As EdmItemCollection = New EdmItemCollection("storage.csdl")
Dim mappingCollection As StorageMappingItemCollection = New StorageMappingItemCollection(conceptualCollection, storageCollection, "storage.msl")
Dim workspace = New MetadataWorkspace()
workspace.RegisterItemCollection(conceptualCollection)
workspace.RegisterItemCollection(storageCollection)
workspace.RegisterItemCollection(mappingCollection)
Dim connectionData = New EntityConnectionStringBuilder(connString)
Dim connection = DbProviderFactories.GetFactory(connectionData.Provider).CreateConnection()
connection.ConnectionString = connectionData.ProviderConnectionString
myEntityConnection = New EntityConnection(workspace, connection)
Return New ObjectContext(myEntityConnection)
Catch ex As Exception
End Try
End Function
Make sure that the storageNS namespace hardcoded value matches the one used in your code, you can view this by debugging the code and examining the storageXML variable to see what was actually used.
Now you can pass a new schema name, and different database connection info at runtime when you create your entities. No more manual .edmx changes required.
Using Context As New JDE_Entities(CreateObjectContext("NewSchemaNameHere", ConnectionString_EntityFramework("ServerName", "DatabaseName", "UserName", "Password"), "JDE_Model"))
Dim myWO = From a In Context.F4801 Where a.WADOCO = 400100
If myWO IsNot Nothing Then
For Each r In myWO
Me.Label1.Text = r.WADL01
Next
End If
End Using
These were the .net libraries used:
Imports System.Data.Entity.Core.EntityClient
Imports System.Xml
Imports System.Data.Common
Imports System.Data.Entity.Core.Metadata.Edm
Imports System.Reflection
Imports System.Data.Entity.Core.Mapping
Imports System.Data.Entity.Core.Objects
Imports System.Data.Linq
Imports System.Xml.Linq
Hope that helps anyone out there with the same issues.
I had a lot of problems getting this to work when using EF6 with an OData Data Service, so I had to find an alternate solution. In my case, I didn't really need to do it on the fly. I could get away with changing the schema when deploying to some test environments, and in the installer.
Use Mono.Cecil to rewrite the embedded .ssdl resources straight in the DLLs. This works just fine in my case.
Here is a simplified example of how you can do this:
var filename = "/path/to/some.dll"
var replacement = "Schema=\"new_schema\"";
var module = ModuleDefinition.ReadModule(filename);
var ssdlResources = module.Resources.Where(x => x.Name.EndsWith(".ssdl"));
foreach (var resource in ssdlResources)
{
var item = (EmbeddedResource)resource;
string rewritten;
using (var reader = new StreamReader(item.GetResourceStream()))
{
var text = reader.ReadToEnd();
rewritten = Regex.Replace(text, "Schema=\"old_schema\"", replacement);
}
var bytes = Encoding.UTF8.GetBytes(rewritten);
var newResource = new EmbeddedResource(item.Name, item.Attributes, bytes);
module.Resources.Remove(item);
module.Resources.Add(newResource);
}