Feed Icon  

Contact

  • Bryant Likes
  • Send mail to the author(s) E-mail
  • twitter
  • View Bryant Likes's profile on LinkedIn
  • del.icio.us
Get Microsoft Silverlight
by clicking "Install Microsoft Silverlight" you accept the
Silverlight license agreement

Hosting By

Hot Topics

Tags

Open Source Projects

Archives

Ads

Previous Page Page 2 of 5 in the ReportingServices category(RSS) Next Page

Rendering Regret

Posted in Reporting Services | SharePoint at Tuesday, 01 February 2005 02:42 Pacific Standard Time

From Barry's Blog via Serge van den Oever on RenderWebPart:

When things go the way you expect, you want to render an entire batch of HTML. However, you don't want to render any of that HTML in the case of an exception when the logic in your Web Part determines that it should display an error message instead. Things can really get ugly if you render the opening tags for an HTML table and do not properly close them due to an exception. This can upset the high-level rendering logic of the page as well as other Web Parts.

The solution to this problem is to use some technique that allows you to write HTML into a buffer and then send it all in a single batch once you know that all the rendering logic has executed successfully. You can create a string buffer using an instance of the System.Text.StringBuffer class. You can then create an instance of the HtmlTextWriter class and use that to render HTML into the buffer.

Barry then gives some code that will allow you to buffer your HTML output which lets you manage your exceptions gracefully. I liked the idea so much that I've already added it to the latest version of the RsWebParts (version 1.2 which I'm still trying to tidy up for release on sf.net). Since the RsWebParts all inherit from the RsBasePart I simply did this:

        /// 
        /// Renders the HTML contents of the webpart. 
        /// The base webparts calls this and buffers the output in order to 
        /// prevent the webpart causing the SharePoint page to fail. This code
        /// was take from Barry's Blog (http://www.barracuda.net/barrysblog.aspx?Date=10/22/2004)
        /// 
        /// HtmlTextWriter to writer content with.
        protected override void RenderWebPart(HtmlTextWriter output) 
        { 
            // create buffer for output 
            StringBuilder buffer = new StringBuilder(10240);
            StringWriter InnerWriter = new StringWriter(buffer);
            HtmlTextWriter BufferWriter = new HtmlTextWriter(InnerWriter); 
            try 
            { 
                // call method to write HTML to buffer
                WriteWebPartContent(BufferWriter);
                // write HTML btach back to browser
                output.Write(buffer);
            }
            catch(Exception ex) 
            { 
                output.Write("Web Part Error: " + ex.Message); 
            } 
        }
        /// 
        /// The webpart must call this method to render its content instead of
        /// overriding RenderWebPart.
        /// 
        /// HtmlTextWriter to writer content with.
        protected abstract void WriteWebPartContent(HtmlTextWriter output);

Next I just renamed the RenderWebPart methods of each web part to WriteWebPartContent and now I have buffered output! Very cool.

Barry has some other great articles on his blog and his company offers SharePoint training. My boss and one of my coworkers took the class and both learned quite a bit (I didn't take the class because I'm not really the SharePoint admin, just a web part developer).

RsWebParts Update

Posted in Reporting Services | SharePoint at Friday, 28 January 2005 07:42 Pacific Standard Time

Ok, I've created a new project site at SourceForge.net which you can find here. Everytime I try to figure out the CVS stuff I end up feeling dumb because I can't figure it out. So hopefully I'll “get it” this weekend so that I can put some files up there. For now the project site is just an empty shell.

In the meantime I would really like to figure out this reported bug about a security issue with the web parts. I'm not really sure if I understand what the problem is but I don't think it is really an issue with the web parts but rather a configuration issue.

The reason I think it is a configuration issue is because the main web part (the report viewer) is basically just an iframe that loads a report services report. The machine that I test my web parts on  is a different machine than the reporting services server so that shouldn't be an issue. I'm just wondering if the problem is that the user viewing the web parts doesn't have access to the reporting server. But I don't really know so let's talk more about this problem either here in the comments or in the forms.

While we're at it, please post any other bugs you've run into at the project site.

Reporting Services WebParts Space Heats Up

Posted in Reporting Services | SharePoint at Monday, 24 January 2005 02:17 Pacific Standard Time

I haven't done much with the RsWebParts for since I released the last beta version (sorry if you've sent me email or posted questions in the comments, I've been too busy), but it looks like they are getting some more attention with the release of Reporting Services SP2 beta.

One thing that seems to have gone unnoticed is that the new version of BI Portal has been released [via Duncan and Patrick]. What does BIP have to do with Reporting Services WebParts? Well the BIP viewer can view Reporting Services Reports (and the BIP Viewer is a WebPart). It also has a Report Browser. In fact, the BIP viewer was the inspiration for the RsWebParts and you will even see that I borrowed some of my client side scripting from the BIP viewer.

So now there are 4 Reporting Services WebPart Packages (that I know of) so it will be intereting to see what happens next. I would expect the MS WebParts to come out on top unless they don't provide the functionality that people need. I would really like to continue working on the RsWebParts or at least get it out as an open source community project (especially since GotDotNet is so pathetic, right now the workspaces are down and there is no message explaining why or when things will be back).

What would you suggest? Should I create some kind of open source project for the SharePoint/Reporting Services community to extend? If so, how should I go about this?

 

 

RsWebParts 1.1 BETA

Posted in Reporting Services | SharePoint at Wednesday, 20 October 2004 11:13 Pacific Daylight Time

If you're interested in the RsWebParts I just uploaded the installer for the beta of the next version.

RsWebPart Workspace

See the news for the list of fixes and enhancements. This is a beta and so what I'm really looking for is feedback, suggestions, and most of all: bug reports. I'm sure there are going to be some since I put this together pretty quickly.

Update: Post any feedback you have here (or just reply to this post).

Accessing Databases via Custom Code

Posted in Reporting Services | General at Wednesday, 21 July 2004 07:10 Pacific Daylight Time

Another good question in response to my Custom Code in Report Services article.

I am trying to access a database through a custom assembly and I already have the following in the rssrvpolicy.config file , but I still get the #Error. [snip] Do I need an associated permission set? And what would that be if I do, I dont know?

The answer that yes you do need an associated permission set. If you're using the System.Data.SqlClient to connection to a SQL Server database then you will need the SqlClientPermission. This would look like:

<PermissionSet
    class="NamedPermissionSet"
    version="1"
    Name="MyDatabasePermissionSet"
    Description="A special permission set that grants sql access.">
  <IPermission
      class="SqlClientPermission"
      version="1"
      Unrestricted="true"
  />
  <IPermission
      class="SecurityPermission"
      version="1"
      Flags="Execution, Assertion"
  />
</PermissionSet>

So where did I get this information from? I already knew that the SqlClientPermission was what I needed from some previous projects and I was able to determine what the IPermission section should look like based on Lamont's post. However, this brings up something that I've been unable to figure out: how do you figure out what should be in the IPermission section?

Maybe I'm just missing it but I read through a few CAS articles and I've searched through the documentation and yet somehow this remains a total mystery to me. Is this documented somewhere? Can you point me to it?

The question (from above) continues:

And do I need to do step 3 and 4 of your article for my custom assembly if I am only accessing a database from my function.
And If I do, what would step 3 and 4 be?

Yes you do need to do steps 3 and 4. Step 4 would be exactly the same. In step 3 you would need to assert the SqlClientPermission on your method as follows:

[VB]
<SqlClientPermission(SecurityAction.Assert)> _
Public Shared Function Foo() as String
...

[C#]
[SqlClientPermission(SecurityAction.Assert)]
public static string Foo()
...

I'm going to go back and read up some more on CAS. It is something every .Net developer should understand.

Update:

For some reason when I was testing this I was getting a security exception even though I was asserting SqlClientPermission on the method call. I found I had to actually assert the permission in my code before it would work. The code I used is posted below. I would really like to understand this better but I'm actually heading out of town for a few days with my wife for our 1st wedding anniversary.

[VB]
Dim perm as SqlClientPermission new SqlClientPermission(PermissionState.Unrestricted)
perm.Assert()
'' SQL CODE HERE
CodeAccessPermission.RevertAssert()


[C#]
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Assert();
// SQL CODE HERE
CodeAccessPermission.RevertAssert();

 

Instance Based Custom Code in Reporting Services

Posted in Reporting Services at Wednesday, 21 July 2004 04:14 Pacific Daylight Time
In response to a question posted in the comments of my last article I've created a Writing Custom Code Part II article. In this article I explain the basic of how to use instance based custom code in SQL Server Reporting Services.

Writing Custom Code in SQL Server Reporting Services - Part II

Posted in Reporting Services at Wednesday, 21 July 2004 04:07 Pacific Daylight Time

Writing Custom Code in SQL Server Reporting Services - Part II

In my first article on Custom Code I went through the basics of using custom code. The goal of this article is to answer a question that was asked in the comments of the first article.

Sidney asked:

Could you elaborate on instance based classes and the procedures required to make this work.

So I looked into instance based code to see how it works and it is actually very simple.

Instance Based Code

In the first article we created a simple assembly called MyCustomAssembly that had a class called SayHello. For this article we will add an instance based method to that class and then call it from our report. First let's add the new method to the class. Below is the new code highlighted in bold.

[VB]
Public Class SayHello

  Private counter As Int32 = 0

  Public Function HelloWithCount() As String
    counter += 1
    Return String.Format("Hello! (for the {0} time)", counter)
  End Function

...

[C#]
public class SayHello
{

  private int counter = 0;

  public string HelloWithCount()
  {
    return string.Format("Hello! (for the {0} time)", ++counter);
  }

...

This new method will be instance based and will change the output based on how many times it has been called. So if we call HelloWithCount() three times we would expect the third call to output "Hello! (for the 3 time)".

After you've added the new method re-compile the project. You will need to copy the dll file again to the C:\Program Files\Microsoft SQL Server\80\Tools\Report Designer folder on your computer and overwrite the old version. Next open the report you're using to test the dll and add a reference to the dll (instructions here) if you haven't already done so in the last article. This time we also need to add an entry in the Class section.

In the class section add a new entry by clicking the the class name box and typing "MyCustomAssembly.SayHello" without the quotes. Next click in the instance box next to it and type "hello" without the quotes. This basically adds a variable named hello to the report of the type SayHello. So when the report is run a new instance of this object will be created (even if you don't access the object).

Now that we have an instance of our object we can make calls to it. Add three textboxes to your report and put "=Code.hello.HelloWithCount()" in each one. When you click the preview tab your report should run and you should see a different output in each box (1,2, and 3). You now have used instance based code in your report.

That's it! Hopefully you were able to get everything to work. Now you should have a basic understanding of how to use instance based code with reporting services. If you have a question leave a comment and I will try to address it. Thanks again for the great comments on my last article!

Introduction to Writing Custom Code in RS

Posted in Reporting Services at Friday, 16 July 2004 05:38 Pacific Daylight Time

I just published a short introductory article about Writing Custom Code in Reporting Services.

Writing Custom Code in SQL Server Reporting Services

Posted in Reporting Services at Friday, 16 July 2004 03:24 Pacific Daylight Time

Writing Custom Code in SQL Server Reporting Services

Someone asked me today how to use a Custom Assembly in Reporting Services. Since I'd never used one before I tried to point them to some useful articles that I found here on MSDN. However, the article didn't seem to help much so I decided to look into it and figure out how to do this since it is an interesting topic.

Let me start by saying that I found the documentation on this to be pretty poor. The docs (which can be found here) do you give enough information to figure it out, but they leave out the information that would make the process as simple as it should be. So that is the goal of this article, to show you how easy it really is to write custom code for SQL Server Reporting Services.

Embedded Code

For our first trick we will write some embedded code. To get started open your browser to this page. On that page under embedded code you will see the following statement.

To use code within a report, you add a code block to the report. This code block can contain multiple methods. Methods in embedded code must be written in Visual Basic .NET and must be instance based.

I'm assuming that "instance based" means the methods don't have to be shared (that's static for you C# coders). However, in my tests I found you can use either instance based or shared methods.

So let's get started and add some embedded code to a report. Fire up Visual Studio and create a new report project and a new report (Right click the project -> Add New Item -> Report). Next click this link for instuctions to add the embedded code to the report.

To add code to a report

  1. On the Report menu, click Report Properties.
  2. Note If the Report menu is not available, click within the report design area.
  3. On the Code tab, in Custom Code, type the code.


So type the code :)

It actually is that easy. So for our example type the following:

Public Function SayHello() as String
  Return "Hello from Embedded Code"
End Function

Next add a textbox to the new report put "=Code.SayHello()" without the quotes in the textbox. You should now be able to preview your report and see "Hello from Embedded Code" on the report. That is as far as we are going to take embedded code (I also found this article which talks a little more about embedded code). At this point you should be able to deploy your report to the server and run it on the server.

Custom Assemblies

The next topic is custom assemblies. This is a much more useful feature since you aren't constrained to writing code in VB.Net on a small dialog in the report designer. You can use Visual Studio and your language of choice. So for our example create a new Visual Studio Class Library project in either C# or VB.Net. Call it MyCustomAssembly. Next rename Class1 to SayHello. To the SayHello class we will add one method which is shown below.

[VB]
Public Shared Function Hello() as String
  Return "Hello from My Custom Assembly!"
End Function

[C#]
public static string Hello()
{
  return "Hello from My Custom Assembly!";
}

Once you're done with the code go ahead and compile the project. Once you've gotten your assembly compiled follow this link for instructions on how to add a reference to your code. Once you've added the reference to your code you will also need to copy the dll file to the C:\Program Files\Microsoft SQL Server\80\Tools\Report Designer folder on your computer. The dll file can be found in your project folder under the bin\debug folder (or bin\release if you compiled a release build).

Note: You only need to add a reference. The Class section will only be used if you're class has instance methods (versus Shared or static methods).

Add another textbox to your report and this time fill it with "=MyCustomAssembly.SayHello.Hello()". You should be able to preview the report and see "Hello from My Custom Assembly" on the report. So you have now used both embedded code and custom assemblies. Before you can deploy the report you need to copy the MyCustomAssembly.dll to the server and put it in the C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer\bin folder on the server. Once you have copied the dll file you should be able to deploy your report to the server.

But what if you want to actually do something interesting like grab values from a database or use a web service? To do that you will need to modify the Code Access Security (CAS) settings for reporting services.

Code Access Security

There is actually a comprehensive article on this topic here. I would recommend you read that article. There is also a good introduction to CAS in general here and here. The goal of this article (the one you're reading) is to give you a quick-and-easy example that can get you started. So we will attempt to read a value from a file and return it.

First create a text file called hello.txt and put it in your c:\temp folder. In the file add the text "Hello from a text file!" and save and close the file. Next add the following method to your SayHello class:

[VB]
Public Shared Function HelloFromFile() as String
  Dim reader as StreamReader = New StreamReader("c:\temp\hello.txt")   Dim hello as String = reader.ReadToEnd()
  reader.Close()
  Return hello
End Function

[C#]
public static string HelloFromFile()
{
  using (StreamReader reader = new StreamReader("c:\\temp\\hello.txt"))
  {
    return reader.ReadToEnd();
  }
}

Note: You will also need to add Imports System.IO in VB and using System.IO in C#.

Next compile you the project. Once the project is compiled you will need to remove the old reference in your report and add the new reference and copy pver the new dll (you will have to close the report project to do this). Add a new textbox to the report and fill it with "=MyCustomAssembly.SayHello.HelloFromFile()". You should be able to preview the report and see the text that you added to the text file.

Next copy the dll and the text file (and put the text file in the c:\temp folder) to the server and deploy the report to the server. When you try to view the report on the server you won't see your message. Instead you should see "#Error".

In order to make this work on the server we need to follow the steps outlined in the CAS article. The first step according to the article is to:

1. Identify the exact permissions that your code needs in order to make the secured call. If this is a method that is part of a .NET Framework library, this information should be included in the method documentation.


Since we are using the StreamReader object we should be able to find this information in the StreamReader documentation but unfortunately I wasn't able to find it (at least I didn't see it). However, I'm going to guess (and I guessed right) it requires the FileIOPermission from the System.Security.Permissions namespace.

Next we need to:

2. Modify the report server policy configuration files in order to grant the custom assembly the required permissions.


So following the example given in the article on CAS we can add the following Xml under the <NamedPermissionSets> node in the rssrvpolicy.config file in the C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer folder.

Note: Be careful when you edit this file. I would highly recommend making a backup copy of the original file before making any changes. This file is an Xml file and you must be sure that what you add is valid Xml.

<PermissionSet
    class="NamedPermissionSet"
    version="1"
    Name="HelloFilePermissionSet"
    Description="A special permission set that grants read access to my hello file.">
  <IPermission
      class="FileIOPermission"
      version="1"
      Read="C:\temp\hello.txt"
  />
  <IPermission
      class="SecurityPermission"
      version="1"
      Flags="Execution, Assertion"
  />
</PermissionSet>

Next we need to add the code group. Again, based on the example in the CAS article we can add the following Xml. We have to put this under the correct CodeGroup node in order for it to work correctly. So you will need to put it under the last code group, but make sure it has the same parent node as the last code group. To do this just insert it before the second to last ending CodeGroup as shown below:

...
      />
      </CodeGroup>
[Insert Here]
    </CodeGroup>
  </CodeGroup>
</PolicyLevel>
...

Here is the code group to add.

<CodeGroup
    class="UnionCodeGroup"
    version="1"
    PermissionSetName="HelloFilePermissionSet"
    Name="MyCustomAssemblyCodeGroup"
    Description="A special code group for my custom assembly.">
  <IMembershipCondition
        class="UrlMembershipCondition"
        version="1"
        Url="C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer\bin\MyCustomAssembly.dll"
  />
</CodeGroup>

Once you've added all this you will still need to complete steps 3 & 4 before the custom assembly will work.

3. Assert the required permissions as part of the method in which the secure call is made. This is required because the custom assembly code that is called by the report server is part of the report expression host assembly which runs with Execution by default. The Execution permission set enables code to run (execute), but not to use protected resources.

4. Mark the custom assembly with the AllowPartiallyTrustedCallersAttribute. This is required because custom assemblies are called from a report expression that is a part of the report expression host assembly, which by default is not granted FullTrust, thus is a 'partially trusted' caller. For more information, see "Using Strong Named Custom Assemblies," earlier in this document.


To complete step 3 you will need to first add using System.Security.Permissions or Imports System.Security.Permissions to your SayHello class. Next you will need to assert the FileIOPermission on the HelloFromFile method as shown below:

[VB]
<FileIOPermissionAttribute(SecurityAction.Assert, Read="c:\temp\hello.txt")> _
Public Shared Function HelloFromFile() as String
...

[C#]
[FileIOPermissionAttribute(SecurityAction.Assert, Read="c:\\temp\\hello.txt")]
public static string HelloFromFile()
...

Next in your assembly attribute file (AssemblyInfo.cs in C# or AssemblyInfo.vb in VB), add the following assembly-level attribute (and add Imports System.Security or using System.Security):

[VB]
<assembly:AllowPartiallyTrustedCallers>

[C#]
[assembly:AllowPartiallyTrustedCallers]

Once you've done this, recompile the assembly and copy it to the server. You should now be able to execute the report and see "Hello from a Text File!" instead of "#Error". If you still see the error need to make sure that you click the refesh button on the report toolbar and you may have to restart the reporting service.

That's it! Hopefully you were able to get everything to work. Now you should have a basic understanding of how to use custom code with reporting services. If you have drop leave a comment and I will try to address it.

Impersonating WebPart

Posted in Reporting Services | SharePoint at Wednesday, 30 June 2004 10:07 Pacific Daylight Time

First an insensitive validator and next an Impersonating WebPart. What is web development coming too? :)

Jay Nathan now has a .Text blog! After you subscribe you should read his article on the Impersonating WebPart. It covers the basics of how to use the WebPart to impersonate users along with an overview of things like CAS.

If you're new to SharePoint development and you don't know what CAS stands for then you really need to read Maxim Karpov's article on the subject. Jan also talks about it in reference to the SmartPart here. While CAS is not something I really enjoy, it is essential to understand. If you don't at least understand the theory then you're going to run into real trouble the first time you get a security exception in the web part you're developing.

But back to the article. One thing that caught my attention since it is something that I've struggled with before:

Impersonator i = new Impersonator("SharePointRead", "MARINER", 
"password123").Impersonate();

// Code requiring higher permissions...

i.Undo();

Note: Instead of hard-coding the account credentials, they should probably be stored in the SharePoint's web.config file or a SQL Server table.

I really don't want to put the username and password in the code (as noted), but I also really don't want to put them in a database or a web.config file either. What I really want to do is figure out how to make this work with single sign-on. Unfortunately I haven't even got single sign-on running so I haven't looked at if this is possible.

Overall I think the Impersonating WebPart is a great idea and will probably cover 99% of what you would use it for. However, there are some cases (like Reporting Services) where I think you might need to have something like single sign-on in order to deal with who sees what data.

Previous Page Page 2 of 5 in the ReportingServices category(RSS) Next Page