Service & Data Contracts

SsrsRenderStudio.Wcf.ServiceContract.IRenderStudioUserService

Method Purpose
ExecutionResult PrintReport(ExecutionContext context) Sends a report to a printer destination
ExecutionResult EmailReport(ExecutionContext context) Sends a report to an email destination in PDF format
bool ReprocessReport(Guid Id) Quickly reprocess an already processed report


ExecutionContext
Member Type Definition
FriendlyReportName string Name of the report to print. Must match master configuration.
Destination string Either the printer name or email addresses delimited with a semicolon. Printer name must match master configuration.
ReportParameters string [] Report execution parameters. An even numbered string array of values. First value being the parameter name and the second being the parameter value. (e.g. string[] {"param1", "abc", "param2", "123"})
ClientInformation string[] Any user defined array of strings to include for logging purposes


ExecutionResult
Member Type Definition
IsSuccess bool Was execution an overall success.
ExecutionId Guid Can be used to reprocess the report.
ReportExpiration DateTime When the report expires from cache.
ServiceSideException EnumServiceSideException An enumerated exception value.
ServiceSideExceptionAddl string Additional exception details.

Sample Client Code

public ReportSubmitResponse SubmitReport(ReportSubmitRequest report)
        {
            ExecutionResult result = null;
            IRenderStudioUserService proxy = null;
            CustomClientFactory<IRenderStudioUserService> factory = null;

            string endpointConfiguration = "*";
            bool success = false;
            bool abort = false;
            bool shouldAttemptRecovery = false;

            bool isPrintDestination = false;
            string userDestination = "";

            // todo: determine the destination and the output type

            // prepare context
            string[] a = new string[report.ReportParameters.Length + 4];
            string[] b = new string[] { "ReportingService", report.EmployeeDivision, report.EmployeeBadge, report.ReportFriendlyName };
            b.CopyTo(a, 0);
            report.ReportParameters.CopyTo(a, b.Length);
            string[] clientInformation = a;
            ExecutionContext context = new ExecutionContext(report.ReportFriendlyName, userDestination, report.ReportParameters, clientInformation);

            // keep track of the first endpoint in the config pointer so we don't loop endlessly through the binding configs
            System.ServiceModel.Description.ServiceEndpoint firstEndpoint = null;

            do 
            {
                // reset loop flag
                shouldAttemptRecovery = false;

                try
                {
                    // submit report
                    factory = new CustomClientFactory<IRenderStudioUserService>(endpointConfiguration);

                    if (!factory.ClientConfiguredSuccessfully)
                    {
                        shouldAttemptRecovery = true;
                        throw new Exception("Failed to configure client");
                    }

                    if (firstEndpoint == null)
                        firstEndpoint = factory.Endpoint;
                    else
                        if (firstEndpoint.Address.Uri == factory.Endpoint.Address.Uri)
                            throw new Exception("Client tried all possible bindings and could not establish a connection. Aborting.");
                    try
                    {
                        Logger.Log(EnumLoggingSeverity.DEBUG, "ReportingServiceImpl: attempting " + factory.Endpoint.Address.Uri.ToString());
                    }
                    catch (Exception ex1)
                    {
                        Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: Factory failed to retrieve a binding configuration pointer.");
                    }

                    proxy = factory.CreateChannel();

                    if (isPrintDestination)
                        result = proxy.PrintReport(context);
                    else
                        result = proxy.EmailReport(context);

                    success = result.IsSuccess;
                }
                catch (CommunicationException ex)
                {
                    abort = true;
                    shouldAttemptRecovery = true; // we should always try to recover on a comm error
                    Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: CommunicationException, " + ex.Message);
                }
                catch (TimeoutException ex)
                {
                    abort = true;
                    Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: TimeoutException, " + ex.Message);
                }
                catch (Exception ex)
                {
                    abort = true;
                    Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: Exception, " + ex.Message);
                }
                finally
                {
                    if (proxy != null)
                    {
                        if (abort)
                            ((IClientChannel)proxy).Abort();
                        else
                            ((IClientChannel)proxy).Close();
                    }
                }

            } while (shouldAttemptRecovery);

            return new ReportSubmitResponse { IsSuccess = success };
        }

Another sample client code

namespace ReportingService
{
    //todo: document
    //  adding a division:
    //      a DEFAULT user needs to be created in the db for that division to handle all reports when a specific user does not exist. preferably with a print destination
    //          if the DEFAULT user does not exist then the report will fail!
    //      a DEFAULT report record needs to be created in the db for each division
    //          add record with userid and friendlyreportname="*", this will handle any and all reports for that user
    //  destination types:
    //      PRINT, EMAIL
    //  adding users:
    //      inititally there are no users in the DB, any report submitted will print to the DEFAULT user destination
    //      add record to user and report table
    //      the report table needs to include records for all the supported ssrs reports
    //          this might be difficult to maintain, the other way would be to have a modules table such as the 02_ford_mixed_load module and definied the reports there, but actually that wouldn't work to set a print destination based on module because each individual report might need to go to different destionatins.  


    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
    public class ReportingServiceImpl : IReportingService
    {
        public ReportingServiceImpl()
        {
            /*
            Database.SetInitializer(new DbContext_09_ReportingServiceConfiguration_Initializer());
            DbContext_09_ReportingServiceConfiguration db = new DbContext_09_ReportingServiceConfiguration("DbContext_09_ReportingServiceConfiguration_production_seed_only");
            db.Database.Initialize(true);
            db.Dispose();
            */
        }

        public ReportSubmitResponse SubmitReport(ReportSubmitRequest report)
        {
            DbContext_09_ReportingServiceConfiguration db = new DbContext_09_ReportingServiceConfiguration();

            ExecutionResult result = null;
            IRenderStudioUserService proxy = null;
            CustomClientFactory<IRenderStudioUserService> factory = null;

            string endpointConfiguration = "*";
            bool success = false; // the current report being processed 
            bool allSuccess = true; // every single report in this batch
            bool abort = false;
            bool shouldAttemptRecovery = false;

            //******* query DB **********//

                //*** get user
                Model_09_ReportingServiceConfiguration_User db_user = db.GetUser(report.EmployeeDivision, report.EmployeeBadge);

                if (db_user == null)
                    db_user = db.GetUser(report.EmployeeDivision, "DEFAULT");

                if (db_user == null)
                {
                    Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: DEFAULT user in the [users] table for division '" + report.EmployeeDivision + "' does not exist!  Unable to determine the destination.  Unable to process report request.");
                    return new ReportSubmitResponse { IsSuccess = false };
                }

                //*** get report
                //todo: process report to default user if necessary or wildcard destination, report record is null when a wildcard destination is selected
                List<Model_09_ReportingServiceConfiguration_Report> db_reports = db.GetReports(db_user, report.ReportFriendlyName);

                db.Dispose();

                // add a destination for the user based on their email
                if (db_reports.Count == 0)
                {
                    Logger.Log(EnumLoggingSeverity.WARNING, "ReportingServiceImpl: " + report.ReportFriendlyName + " report is not explicitly defined for user " + db_user.Badge + ", division " + db_user.Division + ".");
                    db_reports.Add(new Model_09_ReportingServiceConfiguration_Report
                                    {
                                        DestinationFriendlyName = db_user.Email,
                                        DestinationType = new Model_09_ReportingServiceConfiguration_DestionationType { DestinationName = "EMAIL" }
                                    });
                }

                // override destination with one supplied in service call
                //todo: only print override is supported.
                if( !(string.IsNullOrEmpty(report.DestinationFriendlyName.Trim())) )
                {
                    foreach(var r in db_reports)
                    {
                        //if (r.DestinationType.DestinationName == "PRINT")
                        //{
                            r.DestinationFriendlyName = report.DestinationFriendlyName.Trim();
                            r.DestinationType.DestinationName = "PRINT";
                        //}
                    }
                }

                // prepare context
                string[] a = new string[report.ReportParameters.Length + 4];
                string[] b = new string[] { "ReportingService", report.EmployeeDivision, report.EmployeeBadge, report.ReportFriendlyName };
                b.CopyTo(a, 0);
                report.ReportParameters.CopyTo(a, b.Length);
                string[] clientInformation = a;

                //todo: this needs to be rewritten
                //      if this service fails to fetch HTTP config or fails to write to disk then we're screwed and never get out of the loop
                //      for now we 're limiting the retries so we eventually skip the loop
                int recoveryCount = 0;
                int recoveryCountMAX = 10;

                foreach (var db_report in db_reports)
                {
                    ExecutionContext context = new ExecutionContext(report.ReportFriendlyName, db_report.DestinationFriendlyName, report.ReportParameters, clientInformation);

                    //todo: there should be a better way of doing this, 
                    //  if the admin puts the same endpoint twice then it will abort prematurely.
                    //  also if a binding pointer config does not exist that could be a problem
                    System.ServiceModel.Description.ServiceEndpoint firstEndpoint = null;
                    WcfConfiguration.Reset();
                    recoveryCount = 0;

                    do
                    {
                        // reset loop flag
                        shouldAttemptRecovery = false;
                        
                        try
                        {
                            // submit report
                            factory = new CustomClientFactory<IRenderStudioUserService>(endpointConfiguration);
                            
                            if (!factory.ClientConfiguredSuccessfully)
                            {
                                recoveryCount++;
                                if (recoveryCount < recoveryCountMAX)
                                    shouldAttemptRecovery = true;   
                                
                                throw new Exception("Failed to configure client");
                            }

                            if (firstEndpoint == null)
                                firstEndpoint = factory.Endpoint;
                            else
                                if (firstEndpoint.Address.Uri == factory.Endpoint.Address.Uri)
                                    throw new Exception("Client tried all possible bindings and could not establish a connection. Aborting.");
                            try
                            {
                                Logger.Log(EnumLoggingSeverity.DEBUG, "ReportingServiceImpl: attempting " + factory.Endpoint.Address.Uri.ToString());
                            }
                            catch (Exception ex1)
                            {
                                Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: Factory failed to retrieve a binding configuration pointer.");
                            }

                            proxy = factory.CreateChannel();

                            if (db_report.DestinationType.DestinationName != "EMAIL")
                                result = proxy.PrintReport(context);
                            else
                                result = proxy.EmailReport(context);

                            success = result.IsSuccess;
                        }
                        catch (CommunicationException ex)
                        {
                            abort = true;
                            shouldAttemptRecovery = true; // we should always try to recover on a comm error
                            Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: CommunicationException, " + ex.Message);
                        }
                        catch (TimeoutException ex)
                        {
                            abort = true;
                            Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: TimeoutException, " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            abort = true;
                            Logger.Log(EnumLoggingSeverity.ERROR, "ReportingServiceImpl: Exception, " + ex.Message);
                        }
                        finally
                        {
                            if (proxy != null)
                            {
                                if (abort)
                                    ((IClientChannel)proxy).Abort();
                                else
                                    ((IClientChannel)proxy).Close();
                            }
                        }

                    } while (shouldAttemptRecovery );

                    if (success)
                    {
                        //todo: cache it
                        // todo: maintain user cache
                    }
                    else
                    {
                        allSuccess = false;
                    }
                }

            return new ReportSubmitResponse { IsSuccess = allSuccess };
        }

        public ReportCacheResponse QueryCachedReports(ReportCacheRequest cache)
        {
            return new ReportCacheResponse 
                { 
                    UserCache = new UserCacheObject[] 
                                    { new UserCacheObject 
                                        { 
                                            ExecutionId = Guid.Empty, 
                                            Expiration = DateTime.Now, 
                                            FriendlyReportName = "Your cache is empty!" 
                                        }
                                    } 
                };
        }


        public ReportSubmitResponse ProcessCachedReport(ReportCacheReprocessRequest execution)
        {
            throw new NotImplementedException();
        }
    }
}


Last edited Aug 21, 2016 at 12:58 PM by fixitchris, version 14

Comments

No comments yet.