Help get this topic noticed by sharing it on Twitter, Facebook, or email.

blobs in reports for database-per-tenant applications

I have a multi-tenant application where I override the dataaccess class to generate a custom connection string with the matching database name according to the current user making the request (COT database per tenant example). I have SQL database table with picture field. Picture works correctly on the forms, but not on the reports.

When the report handler tries to load the image using an external url, the image is always missing on the report. I traced the blob.cs handler code and noticed there is no authenticated user, and it seems to be a different session, so it cannot get the blob from the correct database.

It might be possible with the sub-domain per tenant solution, but we cannot create sub-domains for each.

COT, any ideas to get this working?
1 person has
this problem
+1
Reply
  • Hi Allen,
    I've found that sometimes the string in the picture field expression in the report can get changed, especially if you've copied and pasted the picture field.

    I would double-check that the expression in the report picture field is looking at the same data as your picture field in your form.

    For example, here is the data that feeds the picture in the FORM;


    Here is what the string in the REPORT picture field should look like;

    =CStr(Parameters!BaseUrl.Value) & "/Blob.ashx?YOURPictureBlobHandler=t|" & CStr(Fields!ID.Value)

    Also, make sure the "ID" field is actually in the report data.

    Cheers,

    Jonesy
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated kidding, amused, unsure, silly indifferent, undecided, unconcerned happy, confident, thankful, excited

  • Thanks for the suggestion. I know what you mean about the report field value, I double checked and it's correct.

    Calls to /Blob.ashx within the application are authenticated, so they are working. But calls to /Blob.ashx from within the report handler are not authenticated, I'm not sure why. So the database-per-tenant code isn't working because it's based on the authenticated user. I'm sure this would work if it wasn't multi-tenant, but my application is for multiple customers.
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated indifferent, undecided, unconcerned kidding, amused, unsure, silly happy, confident, thankful, excited

  • I found a simple solution was to change the connection string based on a query string parameter when the request was from blob.ashx and it was not authenticated. I added a parameter to the rdlc file and code to the report handler to supply the parameter value. The report uses the new parameter in the external url of the image.
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated indifferent, undecided, unconcerned kidding, amused, unsure, silly happy, confident, thankful, excited

  • Chris

    I found this post and your reply today. I generated a custom report yesterday but can't get my image to display in the PDF and from what I can see in my settings below all looks correct, any suggestions. I know its displaying the custom report template as I added th xxxxx next to the picture label.
    I also added the ID field into the report but didn't make a difference.
    Steve



  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated kidding, amused, unsure, silly indifferent, undecided, unconcerned happy, confident, thankful, excited

  • It's tricky because you have find a way for DataAccessCustom to get the database name when the Blob handler request is not authenticated. My solution was to pass the database name to the report as a parameter and then append it to the external url of the image control as a query string to the blob handler. Then in DataAccessCustom I check the query string for the database name.

    I was able to get it working by modifying Report.cs (which gets re-generated eventually) somewhere around line 431 after "load report definition"..


    // load report definition
    ...
    ...
    ...
    // custom multi-tenant blob handling for reports..
    if (request.Controller.Equals("MyController") && reportTemplate.Contains("MyControllerPicture"))
    {
    // inject a new parameter for the databas name to use
    reportTemplate = reportTemplate.Replace(
    "</ReportParameters>",
    " <ReportParameter Name=\"BlobDatabase\">\n" +
    " <DataType>String</DataType>\n" +
    " <DefaultValue>\n" +
    " <Values>\n" +
    " <Value>=String.Empty</Value>\n" +
    " </Values>\n" +
    " </DefaultValue>\n" +
    " <AllowBlank>true</AllowBlank>\n" +
    " <Prompt>Filter Details</Prompt>\n" +
    " <Hidden>true</Hidden>\n" +
    " </ReportParameter>\n" +
    "</ReportParameters>");

    // inject a new value for the image control on the report to include the new parameter
    if (reportTemplate.Contains("MyControllerPicture"))
    reportTemplate = reportTemplate.Replace(@"=CStr(Parameters!BaseUrl.Value) & ""/Blob.ashx?MyControllerPicture=t|"" & CStr(Fields!ID_Vehicle.Value)", @"=CStr(Parameters!BaseUrl.Value) & ""/Blob.ashx?MyControllerPicture=t|"" & CStr(Fields!ID_Vehicle.Value) & ""&MyDatabaseName="" & CStr(Parameters!BlobDatabase.Value)");

    }


    Now in DataAccessCustom.cs, if the request is not authenticated, I get the database name from the query string...


    //not authenticated, check if this is a Blob request
    if (HttpContext.Current.Request.CurrentExecutionFilePath.Contains("Blob.ashx"))
    {
    //check query string for database name
    if (HttpContext.Current.Request.QueryString["MyDatabaseName"] != null)
    {
    // build the new connection string according to the query string variable
    SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder(settings.ConnectionString);
    csb.InitialCatalog = HttpContext.Current.Request.QueryString["MyDatabaseName"].ToString();

    // assign the new connection string
    settings = new ConnectionStringSettings(null, csb.ToString(), settings.ProviderName);
    }
    }
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated indifferent, undecided, unconcerned kidding, amused, unsure, silly happy, confident, thankful, excited

  • I also found this helpful, for multi-tenant database session state, point the COT state provider to a single standalone database. I used the same session state connection string name as the membership database - so that it's easy to check on later, and it's all in one spot.

    You can add these two commands to the web config section of the COT generator:


    Delete: /configuration/system.web/sessionState/providers/add
    [@name="ApplicationSessionState"]
    <empty/>

    AppendChild: /configuration/system.web/sessionState/providers
    <add name = "ApplicationSessionState"
    type="Mycompany.Services.ApplicationSessionState"
    connectionStringName="LocalSqlServer" />


    In DataAccessCustom.cs you need to check if the connection string contains the word "Membership", and then ignore the custom code.


    protected override ConnectionStringSettings CreateSettings(string connectionStringName)
    {
    // initialize using the base class method (setup default database connection)
    ConnectionStringSettings settings = base.CreateSettings(connectionStringName);

    // only non-Membership database requests (membership and session data stored in standlaone database)
    if (!settings.ConnectionString.Contains("Membership"))
    {
    // check for null before checking authenticated requests
    if (HttpContext.Current != null && HttpContext.Current.User != null)
    {
    // only authenticated requests
    if (HttpContext.Current.User.Identity.IsAuthenticated)
    {
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated kidding, amused, unsure, silly indifferent, undecided, unconcerned happy, confident, thankful, excited

  • Allen, thanks. What I find unbelievable is having to go to this length to get this to work. There is nothing in the online help on this and I can see this is going to be a challenge for me to get this to work when it appears this should be something out of the box. I saw a post by COT referring to the Northwind sample and that it “works”!

    What exactly do you mean by authenticated requests as I sign into my application before I can select the PDF report

    I wonder if COT do this work around to get it working in their Northwind application
    Steve
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated indifferent, undecided, unconcerned kidding, amused, unsure, silly happy, confident, thankful, excited

  • It just depends on your own scenario. You might consider using separate domains names per customer, or using a single database with record filtering. You will still need to manage client backup and restore or bulk data integration. COT does a great job all around with the data access, but the multiple database management is something above and beyond.
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated indifferent, undecided, unconcerned kidding, amused, unsure, silly happy, confident, thankful, excited

  • At the moment I am going down the path of one database using record filtering and that is working really well so no issues from that side of things. I did a webex meeting with COT which was great. It wasn’t until I tried producing my first report that I struck this issue which hopefully i will get to work
  • (some HTML allowed)
    How does this make you feel?
    Add Image
    I'm

    e.g. sad, anxious, confused, frustrated indifferent, undecided, unconcerned kidding, amused, unsure, silly happy, confident, thankful, excited