Using Power-Shell in automated tasks (batch script alternative)

Power Shell it’s the Microsoft’s Automation framework sitting on top of .Net (System.Management.Automation) that lets us perform advanced automated tasks. It can help not only the administrators but also the programmers to save a lot of time by automating repeatable processes. The script’s syntax is quite intuitive and easy to learn so it allows users already writing e.g. batch scripts to grasp it very fast.

If we have for example an old batch file that executes sql scripts stored in the folder as follows:

 @ECHO OFF
 echo script folder: %1
 echo server: %2
 echo database: %3
 echo database user: %4
 echo ----------------

setlocal enabledelayedexpansion
cd /d %1

for %%d in (*.sql) do (
   set filecontent=
   for /f "delims=" %%a in (%%d) do (set "filecontent=!filecontent!%%a")
   set "temp=!filecontent!"
   set "filecontent=!temp:#PARAM#=%4!"
   echo executing %%d
   echo ----------
   sqlcmd -S%2 -E -Q"!filecontent!" -d%3 
)
pause

then the above script can be easily replaced with Power-Shell. Using PS gives us the same flexibility of scripting languages and at the same time allows us to interact with .Net framework when required.

Before we can start executing PS scripts on our computer, we need to enable execution which is disabled by default. We can do it using following cmdlets:

 //enable executing 
 Set-ExecutionPolicy RemoteSigned

 //get back to default settings
 Set-ExecutionPolicy Default

After we’ve created test.ps1 file, we can start implementation. In order to make testing easier we can define default params inside the script using param(…) constructor. Please note that by using [string] keyword we actually creating strongly typed objects. Defined parameters will be overwritten by the user’s passed-in params.

ps-script

The first command (dir $ScriptFolder) just lists all files within the directory, then ForEach-Object function iterates through all files getting their content into $query variable. Next the $query text is being replaced with our parameter if we have any. The final task is just to execute SQLCMD command passing in query and database connection settings.

param (
[string]$ScriptFolder = "Database\Procedures",
[string]$Server = ".\SQL2008R2",
[string]$Database = "MyDb",
[string]$DatabaseUser = "user"
)


(dir $ScriptFolder) | 
  ForEach-Object { 
    $query = get-content $_.FullName -Raw
    $query = $query.Replace("#PARAM#","user");
    SQLCMD -S $Server -E -Q $query -d $Database
   }

In order to run our Power-Shell script we need just execute following line:

 //command line
 powershell -noexit C:\test.ps1 -ScriptFolder "C:\Database\Procedures" -Server ".\SQL2008R2" -Database "MyDB" -DatabaseUser "user"

Good luck with your Power-Shell scripting 🙂

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Displaying interactive MS Chart objects in ASP.NET MVC

Displaying charts using MS Chart is usually simple in asp.net webform. The problem exists if we want to use it in Asp.net MVC application without putting it on .aspx page. In this article I will show you how to display interactive chart using purely MVC view.

mschart_mvc

Lets start with creating ChartHelper class that will contain basic logic for creating chart objects. In the function CreateDummyChart() we will just create standard MS Chart object with some dummy data.

 public static Chart CreateDummyChart()
 {
    var chart = new Chart() { Width = 600, Height = 400 };
    chart.Palette = ChartColorPalette.Excel;
    chart.Legends.Add(new Legend("legend1") { Docking = Docking.Bottom });

    var title = new Title("Test chart", Docking.Top, new Font("Arial", 15, FontStyle.Bold), Color.Brown);
    chart.Titles.Add(title);
    chart.ChartAreas.Add("Area 1");

    chart.Series.Add("Series 1");
    chart.Series.Add("Series 2");

    chart.BackColor = Color.Azure;
    var random = new Random();
    
    //add random data: series 1
    foreach (int value in new List<int>() { random.Next(100), random.Next(100), random.Next(100), random.Next(100) })
    {
        chart.Series["Series 1"].Points.AddY(value);

        //attach JavaScript events - it can also be ajax call
        chart.Series["Series 1"].Points.Last().MapAreaAttributes = "onclick=\"alert('value: #VAL, series: #SER');\"";
    }

    //add random data: series 2
    foreach (int value in new List<int>() { random.Next(100), random.Next(100), random.Next(100), random.Next(100) })
    {
        chart.Series["Series 2"].Points.AddY(value);

        //attach JavaScript events - it can also be ajax call
        chart.Series["Series 2"].Points.Last().MapAreaAttributes = "onclick=\"alert('value: #VAL, series: #SER');\"";
    }

    return chart;
 }

Next, we need to create function that takes our newly created chart object and saves it to memory stream. After that we only need to convert the stream to byte array and to base64 string afterwards.

Having image string created, we simply construct html “img” tag to be rendered by our view. Please note that “data:image/png;base64” attributes are necessary to tell the browser to render it as image.

 public static string GetChartImageHtml(Chart chart)
 {
    using (var stream = new MemoryStream())
    {
        var img = "<img src='data:image/png;base64,{0}' alt='' usemap='#" + ImageMap + "'>";

        chart.SaveImage(stream, ChartImageFormat.Png);

        var encoded = Convert.ToBase64String(stream.ToArray());

        return string.Format(img, encoded);
    }
 }

The final thing is to create DisplayChart() method in the view to be used by Html.RenderAction. Apart from chart’s image string, we also need to display chart’s image map in order to enable chart JavaScript events when needed.

public ActionResult DisplayChart()
{  
    var chart = ChartHelper.CreateDummyChart();
   
    var result = new StringBuilder();
    result.Append(ChartHelper.GetChartImageHtml(chart));
    result.Append(chart.GetHtmlImageMap(ChartHelper.ImageMap));

    return Content(result.ToString());
}

And finally this is our view

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>

@{ Html.RenderAction("DisplayChart"); }

I have attached project files to save your time 🙂

mschart-mvc

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Using WCF REST service in server and client side calls

When using WCF services sometimes there is a need to use REST architecture, especially for web applications. Using REST, brings a lot of benefits such as: broad caching possibilities, interoperability, simplicity etc. Also using REST with JSON instead of soap/xml based protocol decreases network bandwidth and makes it easier to utilize by JavaScript Ajax calls.

In this article I will show you how to retrieve and update data from WCF restful service using JSON protocol. In order to do that we will build request helper to wrap-up our requests sent from MVC application. We will also use JSONP data format to make cross-domain ajax calls to WCF service.

wcf_rest_json_calls

Let’s create MVC application and IIS hosted WCF service. In order to apply custom routing to our service, we will have to change following settings:
– set aspNetCompatibilityEnabled=”true” in WCF web.config file,
– set [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] – to our service class,

We also need to set crossDomainScriptAccessEnabled=”true” in WCF web.config for our ajax calls from different domain (different localhost port).

Please see WCF web.config settings:

 <?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <authentication mode="None"/>
  </system.web>
  <system.serviceModel>
     <bindings>
        <webHttpBinding>
          <binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true" />
        </webHttpBinding>
      </bindings>
    <services>
      <service name="WCFRestfulService.MyService" behaviorConfiguration="RESTBehavior">
        <endpoint address=""  binding="webHttpBinding" contract="WCFRestfulService.IRestService"
                  behaviorConfiguration="MyEndpointBehavior"  bindingConfiguration="webHttpBindingWithJsonP">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="RESTBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="MyEndpointBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
       multipleSiteBindingsEnabled="true">
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>

Next let’s create our ServiceContract and DataContract. Please note that we are defining UriTemplate to specified url contract routing, we will also use WebMessageFormat.Json attribute to declare the request format.

 [ServiceContract(Namespace = "https://mysite.com/2013/06/08", Name = "WCFRestService")]
 public interface IRestService
 {
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json,
    UriTemplate = "Products?skip={skip}&top={top}")]
    [Description("Returns a list of all products")]
    List<Product> GetAllProducts(int skip, int top);

    [OperationContract]
    [WebInvoke(Method = "DELETE", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json,
    UriTemplate = "DeleteProduct?productID={productID}")]
    [Description("Deletes a product")]
    string DeleteProduct(string productID);

    [OperationContract]
    [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json,
    UriTemplate = "/UpdateProduct")]
    [Description("Updates the product")]
    string UpdateProduct(Product productDto);
 }

 [DataContract]
 public class Product
 {
    [DataMember]
    public string ProductID { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public string Category { get; set; }

    [DataMember]
    public double Price { get; set; }
 }

Also, in the service global file we need to define following routing rule.

 protected void Application_Start(object sender, EventArgs e)
  {
      RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHostFactory(), typeof(MyService)));
  }

Our .svc file should contain following (Factory=”System.ServiceModel.Activation.WebScriptServiceHostFactory” added)

 <%@ ServiceHost Language="C#" Debug="true" Service="WCFRestfulService.MyService" CodeBehind="MyService.svc.cs" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

Inside our service class we will add following test methods:

 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
 public class MyService : IRestService
 {
    List<Product> IRestService.GetAllProducts(int skip, int top)
    {
        var productList = new List<Product>();
        var random = new Random();

        #region simulate database
        for (var i = 0; i < 100; i++)
        {
            productList.Add(new Product()
            {
                ProductID = i.ToString(),
                Category = "Category " + i % 2,
                Name = "Product " + i.ToString(),
                Price = random.Next(10, 100)
            });
        } 
        #endregion

        //////////////////////

        var productSet = (from product in productList
                          orderby product.Name
                          select product).Skip(skip).Take(top).ToList();

        return productSet;
    }

    string IRestService.DeleteProduct(string productID)
    {
        //delete from database here


        return "OK";//return status or error
    }

    string IRestService.UpdateProduct(Product productDto)
    {
        //update the product here


        return "OK";//return status or error
    }

}

Next, in MVC application let’s create request helper that we will use to wrap-up our requests executed from C# controllers. Helper functions simply convert our data to JSON format when sending REST requests. We will also de-serialize objects from JSON to C# when retrieving the data.

 public class RequestHelper
 {
    /// <summary>
    /// Get object using GET
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="url"></param>
    /// <returns></returns>
    public static T GetObjectRest<T>(string url)
    {
        var request = HttpWebRequest.Create(url);
        request.Method = "GET";

        var response = request.GetResponse() as HttpWebResponse;
        using (var resStream = response.GetResponseStream())
        {
            using (var reader = new StreamReader(resStream))
            {
                var jsonResponse = reader.ReadToEnd();

                return FromJSON<T>(jsonResponse);
            }
        }
    }

    /// <summary>
    /// Updates object
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    public static string UpdateObjectRest<T>(string url, T objectDto)
    {
        var request = (HttpWebRequest)WebRequest.Create(url); 
        request.Method = "POST";
        request.ContentType = "application/json";

        //send the data.
        using (var requestWriter = new StreamWriter(request.GetRequestStream()))
        {
            requestWriter.Write(ToJSON<T>(objectDto));
        }

        //read response
        var response = (HttpWebResponse)request.GetResponse();
        using (var resStream = response.GetResponseStream())
        {
            using (StreamReader reader = new StreamReader(resStream))
            {
                return reader.ReadToEnd();
            }
        }
    }

    /// <summary>
    /// Send DELETE request
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    public static string DeleteObjectRest(string url)
    {
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "DELETE";

        //read response
        var response = (HttpWebResponse)request.GetResponse();
        using (var resStream = response.GetResponseStream())
        {
            using (StreamReader reader = new StreamReader(resStream))
            {
                return reader.ReadToEnd();
            }
        }
    }

    /// <summary>
    /// Deserialize json to custom object
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="json"></param>
    /// <returns></returns>
    public static T FromJSON<T>(string json)
    {
        var jss = new JavaScriptSerializer();

        return jss.Deserialize<T>(json);
    }

    /// <summary>
    /// Serializes object to json
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="objectDto"></param>
    /// <returns></returns>
    public static string ToJSON<T>(T objectDto)
    {
        var jss = new JavaScriptSerializer();

        return jss.Serialize(objectDto);
    }
}

Calling Rest service will look as follows:

  //get list of products
  var productList = RequestHelper.GetObjectRest<List<Product>>("https://localhost:9582/Products?skip=0&top=10");

Retrieving data using client side Ajax request is quite difficult because of the cross-domain policy (CORS) disabling web script access by default. The way around that is to use JSONP data format so the server is treating our request as a script call allowing us to return data back inside the call-back function. The limitation of that is that we can only use GET method to retrieve the data. If you need full access to the WCF restful service, it is recommended to make your Ajax requests from the same domain.

 <script type="text/javascript">
    function GetProducts() {
        $.ajax({
            type: "GET", //only 'get' is supported with 'jasonp' datatype
            url: "https://localhost:9582/Products?skip=0&top=10",
            dataType: "jsonp",
            data: '',
            success: function (result) {
                alert('Received: ' + result.length + ' products');
            },
            error: function (xhr, ajaxOptions, thrownError) {
                // alert(xhr.status);
                // alert(thrownError);
            }
        });
    }
</script>

Please find the project files included below.

WCFRestfulService

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Custom RetryPolicy class for Windows Azure transient error handling

According to definition, the Cloud it’s “A set of interconnected servers located in one or more data centres”. As per definition this kind of environment is naturally prone to network latency and other related environmental issues. This is especially true if we are communicating with on premises applications to synchronize and load the data over public network.

In order to ensure that our systems are reliable and functioning correctly within such a environment, we should use RetryPolicy to retry error prone operation when an transient error occurs.

In this article I will show you how to use default RetryPolicy with configuration stored in web.config file. You will also learn how to create custom retry policy class to be used throughout your application for the specific error types you define.

Using retry policy objects simply allows us to retry an operation multiple times at the intervals we configure. Image below shows debugging information when retrying operations that cause the errors:

custom_retry_policy

In our example we will user Microsoft Enterprise Library 5.0 (updated version). To create instances we will use RetryPolicyFactory that can create retry policy objects of following types: AzureCachingRetryPolicy, AzureServiceBusRetryPolicy, AzureStorageRetryPolicy, SqlCommandRetryPolicy, SqlConnectionRetryPolicy. Each type handles specific error types.

The configuration also includes ErrorDetectionStrategy object that is being configured in web.config file:

   <configSections>
    <section name="RetryPolicyConfiguration" type="Microsoft.Practices.EnterpriseLibrary.WindowsAzure.TransientFaultHandling.Configuration.RetryPolicyConfigurationSettings, Microsoft.Practices.EnterpriseLibrary.WindowsAzure.TransientFaultHandling" requirePermission="true"/>
    <section name="typeRegistrationProvidersConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.TypeRegistrationProvidersConfigurationSection, Microsoft.Practices.EnterpriseLibrary.Common"/>
  </configSections>
  <RetryPolicyConfiguration defaultRetryStrategy="Fixed Interval Retry Strategy" defaultSqlConnectionRetryStrategy="Incremental Retry Strategy">
    <incremental name="Incremental Retry Strategy" retryIncrement="00:00:01" initialInterval="00:00:01" maxRetryCount="10"/>
    <fixedInterval name="Fixed Interval Retry Strategy" retryInterval="00:00:01" maxRetryCount="10"/>
    <exponentialBackoff name="Backoff Retry Strategy" minBackoff="00:00:01" maxBackoff="00:00:30" deltaBackoff="00:00:10" maxRetryCount="10" firstFastRetry="false"/>
  </RetryPolicyConfiguration>
  <typeRegistrationProvidersConfiguration>
    <clear/>
    <add name="Caching" sectionName="cachingConfiguration"/>
    <add name="Cryptography" sectionName="securityCryptographyConfiguration"/>
    <add name="Exception Handling" sectionName="exceptionHandling"/>
    <add name="Instrumentation" sectionName="instrumentationConfiguration"/>
    <add name="Logging" sectionName="loggingConfiguration"/>
    <add name="Policy Injection" sectionName="policyInjection"/>
    <add name="Security" sectionName="securityConfiguration"/>
    <add name="Data Access" providerType="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSyntheticConfigSettings, Microsoft.Practices.EnterpriseLibrary.Data"/>
    <add name="Validation" providerType="Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationTypeRegistrationProvider, Microsoft.Practices.EnterpriseLibrary.Validation"/>
    <add sectionName="RetryPolicyConfiguration" name="RetryPolicyConfiguration"/>
  </typeRegistrationProvidersConfiguration>

Let’s create helper class to handle creation of retry policy objects. The first method retrieves the default policy with the configuration stored in web.config file.
We will also attach an Retrying event to log the retry operations.

 public static RetryPolicy GetDefaultPolicy(string name)
 {
    RetryPolicy retryPolicy;

    try
    {
        retryPolicy = RetryPolicyFactory.GetRetryPolicy<StorageTransientErrorDetectionStrategy>(name);
        retryPolicy.Retrying += retryPolicy_Retrying;
    }
    catch (NullReferenceException)
    {
        throw new Exception("Unable to read transient fault handling behaviour from web.config file - section for TransientFaultHandling could be missing.");
    }

    return retryPolicy;
}

 static void retryPolicy_Retrying(object sender, RetryingEventArgs e)
 {
    var message = string.Format(
        "Retry - Count: {0}, Delay: {1}, Exception: {2}",
         e.CurrentRetryCount,
         e.Delay,
         e.LastException.Message);

    Trace.TraceEvent(TraceEventType.Information, 0, message);// write to log
  }

Our next helper function will create custom retry policy. In the constructor we will pass in error types we want to be included in retry operations. Ideally this could be stored in config file.

 public static RetryPolicy GetCustomRetryPolicy()
 {
    var retryPolicy = new RetryPolicy(
        new CustomTransientErrorDetectionStrategy(
            new List<Type>() {
                typeof(DivideByZeroException),
                typeof(IndexOutOfRangeException),
            }),
        new CustomRetryStrategy());

    retryPolicy.Retrying += retryPolicy_Retrying;

    return retryPolicy;
}

Now let’s create custom retry policy class. The most important part of it is ShouldRetry method which simply returns the delegate allowing to evaluate whether to continue retrying operation or not. You can also apply your own logic in this place based on your requirements.

 public class CustomRetryStrategy : RetryStrategy
 {
  private readonly int retryCount = 3;
  private readonly TimeSpan retryInterval = TimeSpan.FromMilliseconds(1000);

  public CustomRetryStrategy()
    : base("customRetryStrategy", true)
  {
    //default values
  }

  public CustomRetryStrategy(int retryCount, TimeSpan retryInterval)
    : base("customRetryStrategy", true)
  {
    this.retryCount = retryCount;
    this.retryInterval = retryInterval;
  }

  public override ShouldRetry GetShouldRetry()
  {
    if (this.retryCount == 0)
    {
        return delegate(int currentRetryCount, Exception lastException, out TimeSpan interval)
        {
            interval = TimeSpan.Zero;

            return false;
        };
    }

    return delegate(int currentRetryCount, Exception lastException, out TimeSpan interval)
    {
        if (currentRetryCount < this.retryCount)
        {
            var random = new Random();
            //set random interval within the threshold
            interval = TimeSpan.FromMilliseconds(random.Next((int)(this.retryInterval.TotalMilliseconds * 0.8), (int)(this.retryInterval.TotalMilliseconds * 1.2)));


			//use your logic here
			//....

            return true;
        }

        interval = TimeSpan.Zero;

        return false;
     };
   }
 }

When creating custom retry policy we also need to create CustomTransientErrorDetectionStrategy class inheriting from ITransientErrorDetectionStrategy interface. In this class we simply evaluate the error type that is currently occurring and need to decide whether retry policy object will attempt to handle it. In order to do that we will pass in our error types to the class constructor. Next, we will check the error type within the IsTransient method to return true if error must cause the retry operation or false otherwise.

 public class CustomTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
 {
    List<Type> exceptionTypesToRetry;

    public CustomTransientErrorDetectionStrategy(List<Type> exceptionType)
    {
        exceptionTypesToRetry = exceptionType;
    }

    public bool IsTransient(Exception ex)
    {
        if (exceptionTypesToRetry.Contains(ex.GetType()))
        {
            return true;
        }
        return false;
    }
 }

Finally in our controller we can test it as follows:

 RetryPolicyHelper.GetDefaultPolicy("Incremental Retry Strategy").ExecuteAction(() =>
 {
     //index out of range exception - it won't be retried
     //as it defaults to sql connection errors
     var array = new int[] { 1, 2, 3 };
     var a = array[5];
 });

 RetryPolicyHelper.GetCustomRetryPolicy().ExecuteAction(() =>
 {
     //divide by zero exception - will be retried
     //as we defined exception types to:
     //DivideByZeroException, IndexOutOfRangeException
     var a = 0;
     var b = 10 / a;
 });

I have included project files below for your tests 🙂

Azure_RetryPolicy

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Adding links and styled text to Power-Point presentation

Formatting paragraphs in OpenXML documents is playing crucial part in proper implementation as the very common requirement is to change PowerPoint files programmatically during the business workflow processes.
I have recently received lot of queries about how to insert formatted text and links into the presentation. Hence, in this article I will show you how to do it.

In the past I have written article about auto generating Power-Point documents from the template file, in this part I will extend the functionality I have implemented before.

power-point-links

Let’s start with defining placeholder paragraphs we will use to insert our formatted texts and links:

 var templ = new PowerPointTemplate();
 templ.PowerPointParameters.Add(new PowerPointParameter() { Name = "[#Paragraph1#]", Text = "Slide 1" });
 templ.PowerPointParameters.Add(new PowerPointParameter() { Name = "[#Paragraph2#]", Text = "Slide 2" });
 templ.PowerPointParameters.Add(new PowerPointParameter() { Name = "[#List1(string[])#]", Text = "test1 n test2 n test3 n test4" });
 templ.PowerPointParameters.Add(new PowerPointParameter() { Name = "[#List2(string[])#]", Text = "test1 n test2 n test3 n test4" });

 templ.PowerPointParameters.Add(new PowerPointParameter() { Name = "[#Link1(#link#)#]", Text = "My link - to Microsoft; https://microsoft.com" });
 templ.PowerPointParameters.Add(new PowerPointParameter() { Name = "[#Link2(#link#)#]", Text = "My link - to Google; https://google.com" });

As you can see, we will handle text lists and single links defined in our template. Of course the names and params will depend on your requirements.

I will skip describing how the process works as this can be read in my previous article. I will go straight to the styled paragraph creation.

Function below returns new paragraph with the styles we have defined in parameters. The RunProperties class is created, then we are assigning the styles – please note that the color needs to be converted to HEX before adding it to run properties. Next we need to define the text-box object containing paragraph’s text. At the end we simply need to append run properties and text to the run and finally append run to the paragraph object.

If you need multiple colors within one paragraph, simply create multiple texts, runs and run properties and configure it separately before assigning it to the paragraph object.

 public Paragraph CreateStyledParagraph(string text, System.Drawing.Color color, bool isBold, bool isItalic, int fontSize = 2000)
 {
    var runProperties = new RunProperties(); //set basic styles for paragraph
    runProperties.Bold = isBold;
    runProperties.Italic = isItalic;
    runProperties.FontSize = fontSize;
    runProperties.Dirty = false;

    var hexColor = color.R.ToString("X2") + color.G.ToString("X2") + color.B.ToString("X2");//convert color to hex
    var solidFill = new SolidFill();
    var rgbColorModelHex = new RgbColorModelHex() { Val = hexColor };

    solidFill.Append(rgbColorModelHex);
    runProperties.Append(solidFill);

    //use this to assign the font family
    runProperties.Append(new LatinFont() { Typeface = "Arial Black" });

    var textBody = new Drawing.Text();
    textBody.Text = text; //assign text

    var run = new Drawing.Run();
    var newParagraph = new Paragraph();

    run.Append(runProperties);//append styles
    run.Append(textBody);//append text
    newParagraph.Append(run);//append run to paragraph

    return newParagraph;
}

Creating links is a little bit different. In order to do it we first need to create HyperlinkRelationship in the slide, the HyperlinkOnClick class contained within the run will then map to it on user click event. Please note that link color is not supported.

 public Paragraph CreateStyledLinkParagraph(SlidePart slidePart, string url, string text, bool isBold, bool isItalic, int fontSize = 2000)
 {
    //note: HyperlinkOnClick does not support link color

    var relationshipId = "rIdlink" + Guid.NewGuid().ToString();//create unique id
    slidePart.AddHyperlinkRelationship(new System.Uri(url, System.UriKind.Absolute), true, relationshipId);//assign hyperlink to the current slide we process

    var runProperties = new RunProperties(
         new LatinFont() { Typeface = "Bodoni MT Black"},
         new HyperlinkOnClick() { Id = relationshipId }); //set basic styles and assign relationshipId
    runProperties.Bold = isBold;
    runProperties.Italic = isItalic;
    runProperties.FontSize = fontSize;
    runProperties.Dirty = false;

    var textBody = new Drawing.Text();
    textBody.Text = text; //assign text

    var run = new Drawing.Run();
    var newParagraph = new Paragraph();

    run.Append(runProperties);//append styles
    run.Append(textBody);//append text
    newParagraph.Append(run);//append run to paragraph

    return newParagraph;
 }

The final replacement is being done in ReplaceText function. See in-line comments:

 void ReplaceText(Paragraph paragraph, SlidePart currentSlide)
 {
    var parent = paragraph.Parent; //get parent element - to be used when removing placeholder
    var dataParam = new PowerPointParameter();

    if (ContainsParam(paragraph, ref dataParam)) //check if paragraph is on our parameter list
    {
        //insert text list
        if (dataParam.Name.Contains("string[]")) //check if param is a list
        {
            var arrayText = dataParam.Text.Split(Environment.NewLine.ToCharArray()); //in our case we split it into lines

            if (arrayText is IEnumerable) //enumerate if we can
            {
                foreach (var itemData in arrayText)
                {
                    //var newParagraph = CloneParaGraphWithStyles(paragraph, dataParam.Name, itemData);// create new param - preserve styles
                    var newParagraph = CreateStyledParagraph(itemData.Trim(), System.Drawing.Color.Green, false, true, 2000);

                    parent.InsertBefore(newParagraph, paragraph); //insert new element
                }
            }
            paragraph.Remove();//delete placeholder
        }
        else if (dataParam.Name.Contains("#link#")) //check if param is a link
        {
            var linkText = dataParam.Text.Split(';')[0].Trim();
            var linkUrl = dataParam.Text.Split(';')[1].Trim();

            var newParagraph = CreateStyledLinkParagraph(currentSlide, linkUrl, linkText, false, true, 1500);
            parent.InsertBefore(newParagraph, paragraph); //insert new element

            paragraph.Remove();//delete placeholder
        }
        else
        {
            //insert text line
            var param = CloneParaGraphWithStyles(paragraph, dataParam.Name, dataParam.Text); // create new param - preserve styles
            parent.InsertBefore(param, paragraph);//insert new element

            paragraph.Remove();//delete placeholder
        }
    }
 }

I have included fully working solution below so you can easily convert it to your needs. I hope this information was useful for you 🙂

PowerPoint-styled-TextLinks

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Using Windows Azure Service Management REST API in automated tasks

Windows Azure platform gives us a lot of new possibilities starting from the ability to auto scale instances of the deployed application to performing an on demand automated changes. When handling multiple applications deployed to the cloud there is a need to automate daily processes in order to save the development time.

In this article I will show you how to automate process of creating new cloud service using Windows Azure REST API. In our example we will create custom Api helper to instantiate our request object that will be then used to invoke the Azure RestFul API procedure.

In order to access WA Api the Azure subscription password and user name is not required, all you need is the subscription ID and the management certificate. This creates the possibility to give some administrative tasks to other people in the company not necessarily having access to the subscription account.

First thing to do is to create and upload management certificate into WA Management Portal. One of the ways to create certificate is to do it from within Visual Studio. In order to do that, we need to right click on our cloud project and open remote desktop configuration wizard. Next we need to select “create new” from the menu. After our certificate is created we can view it and export it to the .cer file. At this stage we also need to read the certificate’s thumb-print that will be used to find it in the local store.

The image below shows the process of configuring new RDP connection and creating new certificate

cert-config-azure

After we have created and exported certificate to the file, we can upload it to the WA Management Portal as shown below

azure-management-certificate

Please note that certificate thumb-print is the same as our local one.

We also need to make sure that our Api helper will find the certificate in our local store. In order to check it’s location, please open Windows Management Console (mmc) and add snap-in for the current user and local computer certificates. Next you need to copy it as depicted below

certificates

At this stage we can start implementing our Api request helper. Let’s create custom PayLoadSettings class first that we will use to hold the basic request settings.

 public class PayLoadSettings
 {
    public string CloudServiceUrlFormat { get; set; }
    public string SubscriptionId { get; set; }
    public string Thumbprint { get; set; }
    public string ServiceName { get; set; }
    public string Label { get; set; }
    public string Description { get; set; }
    public string Location { get; set; }
    public string AffinityGroup { get; set; }
    public string VersionId { get; set; }
 }

Next let’s create function that retrieves our newly created (and uploaded to the WAM portal) certificate from the local machine store

/// <summary>
/// Get certificate from the local machine by thumbprint
/// </summary>
/// <returns></returns>
private X509Certificate2 GetX509Certificate()
{
    X509Certificate2 x509Certificate = null;
    var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    try
    {
        certStore.Open(OpenFlags.ReadOnly);

        var x509CertificateCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, this.PayLoadSettings.Thumbprint, false);

        x509Certificate = x509CertificateCollection[0];
    }
    finally
    {
        certStore.Close();
    }

    return x509Certificate;
 }

Next, we want to create function that inserts our cert into new request object to be sent to execute remote action. We also need to set the requested Api version (not required though).

/// <summary>
/// Create http request object with the certificate added
/// </summary>
/// <param name="uri"></param>
/// <param name="httpWebRequestMethod"></param>
/// <returns></returns>
private HttpWebRequest CreateHttpWebRequest(Uri uri, string httpWebRequestMethod)
{
    var x509Certificate = GetX509Certificate();
    var httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri);

    httpWebRequest.Method = httpWebRequestMethod;
    httpWebRequest.Headers.Add("x-ms-version", this.PayLoadSettings.VersionId);

    httpWebRequest.ClientCertificates.Add(x509Certificate);
    httpWebRequest.ContentType = "application/xml";

    return httpWebRequest;
}

Next step is to create payload document object containing the operation parameters that we want to execute. The names are self-explanatory.

/// <summary>
/// Create payload document
/// </summary>
/// <returns></returns>
private XDocument CreatePayload()
{
    var base64LabelName = Convert.ToBase64String(Encoding.UTF8.GetBytes(this.PayLoadSettings.Label));

    var xServiceName = new XElement(azureNamespace + "ServiceName", this.PayLoadSettings.ServiceName);
    var xLabel = new XElement(azureNamespace + "Label", base64LabelName);
    var xDescription = new XElement(azureNamespace + "Description", this.PayLoadSettings.Description);
    var xLocation = new XElement(azureNamespace + "Location", this.PayLoadSettings.Location);
    var xAffinityGroup = new XElement(azureNamespace + "AffinityGroup", this.PayLoadSettings.AffinityGroup);
    var createHostedService = new XElement(azureNamespace + "CreateHostedService");

    createHostedService.Add(xServiceName);
    createHostedService.Add(xLabel);
    createHostedService.Add(xDescription);
    createHostedService.Add(xLocation);
    createHostedService.Add(xAffinityGroup);

    var payload = new XDocument();
    payload.Add(createHostedService);

    payload.Declaration = new XDeclaration("1.0", "UTF-8", "no");

    return payload;
}

Having payload document created, we can send our request and retrieve request id if operation is successful.

/// <summary>
/// Invoke Api operation by sending payload object
/// </summary>
/// <param name="uri"></param>
/// <param name="payload"></param>
/// <returns></returns>
private string InvokeAPICreateRequest(string uri, XDocument payload)
{
    string requestId;
    var operationUri = new Uri(uri);

    var httpWebRequest = CreateHttpWebRequest(operationUri, "POST");

    using (var requestStream = httpWebRequest.GetRequestStream())
    {
        using (var streamWriter = new StreamWriter(requestStream, UTF8Encoding.UTF8))
        {
            payload.Save(streamWriter, SaveOptions.DisableFormatting);
        }
    }

    using (var response = (HttpWebResponse)httpWebRequest.GetResponse())
    {
        requestId = response.Headers["x-ms-request-id"];
    }

    return requestId;
}

The final function just puts it all together as follows

/// <summary>
/// Execute create cloud service request
/// </summary>
/// <returns></returns>
public string CreateCloudService()
{
    var cloudServiceUrl = string.Format(this.PayLoadSettings.CloudServiceUrlFormat, this.PayLoadSettings.SubscriptionId);
    
    var payload = CreatePayload();

    var requestId = InvokeAPICreateRequest(cloudServiceUrl, payload);

    return requestId;
}

If we will invoke the code from the console, the code should look as below

 static void Main(string[] args)
 {
    //load this from your configuration file
    var payLoadSettings = new PayLoadSettings()
    {
        CloudServiceUrlFormat = "https://management.core.windows.net/{0}/services/hostedservices",
        SubscriptionId = "92533879-88c9-41fe-b24e-5251bcf49a8f",//fake subscription id - please provide yours
        Thumbprint = "3a f6 67 24 d8 d8 b3 71 b0 c4 d3 00 c2 04 0d 62 e5 30 76 1c", //fake cert thumbprint - please provide yours
        ServiceName = "newService1234567",//name your new service
        Label = "newService1234567", //give it a tracking label
        Description = "My new cloud service", //service description
        Location = "North Europe",//select centre
        AffinityGroup = "", //not created yet
        VersionId = "2011-10-01"//api version
    };

    var api = new RestAPIHelper(payLoadSettings);

    try
    {
        var requestId = api.CreateCloudService();

        Console.WriteLine("Cloud service has been created successfully :)" + Environment.NewLine + "Request id: " + requestId);
        Console.ReadLine();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Console.ReadLine();
    }
}

Let’s run the console application now

cloud-service-created

After executing above we can check in WA Management Portal if the cloud service is created. This should look like image below

newcloudservice

I have attached project files for your tests. Please note that you need to set your own configuration settings for it to be working. You can also use above example to create your own automated tasks for Windows Azure – simply implement other operations in similar way. You can then use for example TeamCity to run it automatically when needed. This gives you a lot of possibilities and simply saves your precious development time.

AzureRestAPI

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Claims based authentication on Windows Azure

Claims based authentication can greatly improve user’s experience by allowing them to login to our site using external trusted providers such as Google, Yahoo etc. In this article I will show you how to integrate claims based authentication with existing membership provider. This is common scenario if you don’t have any on premises applications using Active Directory needing to connect to the cloud. If this is the case we would have to use WIF (Windows Identity Foundation) authentication modules and Active Directory Federation Services installed on premises.

In our simple example we will use DotNetOpenAuth to perform claims based authentication along side default membership provider. In order to successfully authenticate the user we will have to correlate user data stored in membership table with the UserID received from external claims callback, in our case we will receive email address as a user-name from Google.

Following diagram presents high level overview of our solution:

claims_authentication

How it works:
User can login to our site as normal using standard login button. If he wishes to login using eg. Google account he will have to click the button below saying ‘Log in with Google’.
After clicking that button, user is redirected to Google’s login page. If the Google login is successful then user is redirected back to our site. Our generic handler validates the claims stored in the response context. If the validation is successful, we are getting email address that we will use to login the user. Before doing that we need to first find the user in our database by email address and get his data.

google_login

Let’s start implementation.

In the Global.asax file we need to attach UserAuthenticated event. We will use it to get callback response from Google containing claimed userID.

 public void RegisterEvents()
 {
    //attach login event from the generic handler
    GoogleLoginHandler.UserAuthenticated += new EventHandler<EventArgs<string>>(GoogleLoginHandler_UserAuthenticated);
 }

 void GoogleLoginHandler_UserAuthenticated(object sender, EventArgs<string> e)
 {
    var accountController = new Controllers.AccountController();
    accountController.LogOnUsingOpenId(e.Data); //pass in email address of the user to be logged in
 }

 protected void Application_Start()
 {
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    RegisterEvents();
 }

Next, lets add following handler GoogleLoginHandler.ashx to our project. In that handler we will redirect to Google login page and read the response after login is successful. See in-line comments:

  public void ProcessRequest(HttpContext context)
 {
	using (var openid = new OpenIdRelyingParty())
	{
		openid.Behaviors.Add(new AXFetchAsSregTransform());

		var response = openid.GetResponse();
		if (response == null)
		{
			// Check if already logged in
			var request = openid.CreateRequest(GoogleOPIdentifier);
			request.AddExtension(new UIRequest { Mode = UIModeDetectSession });
			request.Mode = AuthenticationRequestMode.Immediate;
			request.AddExtension(new ClaimsRequest { Email = DemandLevel.Require });
			request.RedirectToProvider();
		}
		else
		{
			if (response.Status == AuthenticationStatus.Authenticated)
			{
				var claims = response.GetExtension<ClaimsResponse>();
				var userEmail = claims.Email;

				if (UserAuthenticated != null)
				{
					//Log user in
					UserAuthenticated(this, new EventArgs<string>(userEmail));
				}

				context.Response.Redirect(context.Request["ReturnUrl"]);
			}
			else if (response.Status == AuthenticationStatus.SetupRequired)
			{
				var request = openid.CreateRequest(GoogleOPIdentifier);
				request.AddExtension(new ClaimsRequest { Email = DemandLevel.Require });
				request.RedirectToProvider();
			}
			else
			{
				context.Response.Redirect(context.Request["ReturnUrl"]);
			}
		}
	}
 }

We will use following function to redirect to login page and back to destination by reading the ReturnUrl param:

 public static void TryToLogin(string defaultRedirection = "/Home/Index")
{
    //extract return url if exists
    var returnUrl = HttpContext.Current.Request.UrlReferrer != null && HttpContext.Current.Request.UrlReferrer.ToString().Contains("ReturnUrl=") ?
        HttpContext.Current.Request.UrlReferrer.ToString().Split(new string[] { "ReturnUrl=" }, StringSplitOptions.None)[1] : defaultRedirection;
    
    if (returnUrl.Trim().Length == 0) { returnUrl = defaultRedirection; } //enforce default if empty

    HttpContext.Current.Response.Redirect("~/GoogleLoginHandler.ashx?ReturnUrl=" + returnUrl);
}

Our generic handler and declarations will look as follows:

 public class GoogleLoginHandler : IHttpHandler
 {
    public static event EventHandler<EventArgs<string>> UserAuthenticated;

    private const string GoogleOPIdentifier = "https://www.google.com/accounts/o8/id";
    private const string UIModeDetectSession = "x-has-session";

We will also extend EventArgs class to accept generic param that we will use in our callback handler.

 namespace System
 {
    public class EventArgs<T> : EventArgs
    {
        public EventArgs()
        {
        }

        public EventArgs(T data)
        {
            this.Data = data;
        }

        public T Data { get; set; }
    }
}

And finally we call following function to authenticate user containing verified email address that matches email address in our membership table.

/// <summary>
/// Private login method for openId
/// </summary>
/// <param name="emailAddress"></param>
/// <returns></returns>
[Authorize]
internal ActionResult LogOnUsingOpenId(string emailAddress)
{
    var userName = Membership.GetUserNameByEmail(emailAddress);
    if (userName != null)
    {
        FormsAuthentication.SetAuthCookie(userName, true);
    }

    return View();
}

I have included project files below so you can see it working and adjust it to your requirements 🙂

Claims Authentication

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

C# 5.0 Async Programming and Caller Information

Since the release of .Net 4.5 Framework there are new, very useful features added that I want you to get familiar with. Because the C# language evolves along the .Net Framework, I have created chart that will help you to see all major changes that have been introduced over the last 10 years. I still remember myself starting my programming adventure with VS 2002 🙂

c_versions_history

Let’s focus on the new C# 5.0 features now.

Async Framework introduced in C# 5.0 will probably change the the way we do our programming on the .net platform. The idea is to apply standard async procedures in the background while allowing developer to minimize amount of code required.

The new Async logic seems to be incorporated into standard synchronous functions with only a few keywords added (eg. await, async). When marked async, methods are executed synchronously until the first await statement. At this same time the control is given back to the caller allowing executing thread to function correctly (will not freeze UI).

In our example we will call WCF service using async framework. This is mostly accurate real live scenario whether you will use windows forms or WPF application to boost user experience within the UI layer.

async_programming1

After our WCF service is started (self hosting) we are implementing code as follows.

 private async void btnSendWCF_Click(object sender, EventArgs e)
 {
    Task<string> responseTask = SendWCFMessage("test message");

    await Task.Delay(3000); //give it more time

    var response = await responseTask;

    txtResult.Text = response;
}

After executing this methods we are awaiting for the responseTask result while main thread is returned to the caller and UI is fully responsive.

SendWCFMessage method returns task string result. We will use Factory.StartNew to wrap-up our WCF request and return it. See in-line comments.

 Task<string> SendWCFMessage(string message)
 {
    return Task<string>.Factory.StartNew(() =>
    {
        try
        {
            //use TCP when sending message on the same computer or within the trusted network
            var binding = new NetTcpBinding();
            var endpoint = new EndpointAddress(hostEndPointAddress);

            var factory = new ChannelFactory<IServiceHost>(binding, endpoint);
            var channel = factory.CreateChannel();

            //send entered message using created channel
            var result = channel.SendMessage(message);

            //Trace caller information
            TraceMessage(result);

            //return value from the task
            return result;
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
    });
}

As a second example we will use WebRequest class and reuse existing async functionality – the methods GetResponseAsync and CopyToAsync. In both examples we will also display caller trace information.

 private async void btnSendHttpReq_Click(object sender, EventArgs e)
{
    var request = WebRequest.Create(txtUri.Text.Trim());
    var content = new MemoryStream();

    Task<WebResponse> responseTask = request.GetResponseAsync();

    using (var response = await responseTask)
    {
        using (var responseStream = response.GetResponseStream())
        {
            Task copyTask = responseStream.CopyToAsync(content);

            await Task.Delay(3000);//give it more time

            //wait until finished (release main thread in the meantime)
            await copyTask;
        }
    }

    txtResult.Text = string.Format("Response length {0}", content.Length);

    //Trace caller information
    TraceMessage(txtResult.Text);
}

Please notice that in Caller Information “member name” is btnSendHttpReq_Click for webrequest and TraceMessage for WCF as the method is invoked from different thread.

I have attached project files below so you can do your tests as well.

AsyncProgramming

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Using automation for gathering release changes

If you need to gather release changes when creating installers, the script below can help you. Script simply gets all or current database release changes stored in file. I use it to automatically gather the latest changes after TeamCity builds release installer. The changes could be sent to PR for documentation. You can of course use more complicated patterns when extracting script text when required. To execute it, just pass in update script location, version number and email addresses.

release_changes

 

 param (
 [string]$ScriptFolder = "C:\Database\Updates",
 [string]$Version = "2.11.0.0",
 [string]$SendEmailsTo = "test@test.com",
 [boolean]$GetAllChanges = 0
)

#sends notification emails
 Function SendEmail($SendEmailsTo_, $EmailSubject_, $changes_)
 {
   $emailFrom = "teamcity@test.com" 
   $smtpserver="smtp.test.com" 
   $smtp=new-object Net.Mail.SmtpClient($smtpServer) 

   foreach ($email in $SendEmailsTo_.split(';'))
   {   
      $smtp.Send($emailFrom, $email, $EmailSubject_, $changes_)
   }
 }

 #get file by version number
 Function GetReleaseFileName($Version_)
 {
    $VersionFilename_ ="v";
     $Version_.split('.') | ForEach-Object{
        $VersionFilename_ +=  [System.Convert]::ToDouble($_).ToString("00"); 
    }
    $VersionFilename_ += ".sql";

     #format e.g. v12.12.00.10.sql
    return $VersionFilename_;
 }

#return if no emails added
if($SendEmailsTo.trim() -eq "") { "no emails defined!"; return;}

$VersionFilename =  GetReleaseFileName $Version;

$EmailSubject = "Release $Version database changes";
$changes = "Changes in release file $VersionFilename `r`n `r`n";
$hasChanges = 0;

if(!$GetAllChanges) {  
    (dir $ScriptFolder) | sort CreationTime -Descending |
     ForEach-Object {  
        $fileContent = get-content ($_.Directory.FullName + "\" + $VersionFilename) -Raw;

        $patterns = 'alter','drop','insert','delete','update','create'; #paterns/strings to find
        $hasChanges = 0;
        foreach ($pattern in $patterns)
        {
           $fileContent.split("`r`n") | ?  { $_ -match $pattern } | % { $changes += $_.trim() + "`r`n"; $hasChanges = 1; } 
        } 

        if($hasChanges -eq 0) { $changes += "`r`nNo database changes for release $Version !"}

        write-output $changes

        SendEmail $SendEmailsTo $EmailSubject $changes;

        break;        
    }
}
else
{
  #parse all files for all previous releases
  $EmailSubject = "All database changes";
  $changes = "All database changes: `r`n `r`n";

  (dir $ScriptFolder) | sort CreationTime -Descending |
    ForEach-Object {  
        $fileContent = get-content ($_.Directory.FullName + "\" + $_.name) -Raw;

        $patterns = 'alter','drop','insert','delete','update','create';
        $changes += "`r`nChanges in release file $_`r`n";
        $hasChanges = 0;

        foreach ($pattern in $patterns)
        {          
           if($fileContent)
           {
              $fileContent.split("`r`n") | ?  { $_ -match $pattern } | % { $changes += $_.trim() + "`r`n"; $hasChanges = 1; }  
           } 
        } 

        if($hasChanges -eq 0) { $changes += "`r`nNo database changes for release " + $_.name.Replace(".sql","").Replace("v0","");}

        $changes += "`r`n-------------------------------`r`n";      
    }

    write-output $changes 

    SendEmail $SendEmailsTo $EmailSubject $changes;
}

I have included full script sample below
release_changes_notifier

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 4.00 out of 5)
Loading...Loading...

Creating WCF custom transport channel

Primary purpose of using WCF within organization is to wrap-up existing components into service oriented architecture (SOA) that can be then easily reused and maintained. When communicating within the local network or between the local components, you may want to implement your own WCF transport channel satisfying your specific requirements.

In this article I will show you how to write your own transport channel to be used without the contract bindings.

wcf_custom_channel

Lets’s start from the CustomBinding class that needs to implement Binding base. One thing to remember is that in CreateBindingElements method the transport element must be the last in the collection, otherwise you will get exception.

 public class CustomBinding : Binding
 {
    readonly MessageEncodingBindingElement messageElement;
    readonly CustomChannelBindingElement transportElement;

    public CustomBinding()
    {
        this.messageElement = new TextMessageEncodingBindingElement();
        this.transportElement = new CustomChannelBindingElement(); //must be the last in collection
    }

    public override BindingElementCollection CreateBindingElements()
    {
        return new BindingElementCollection(new BindingElement[] {
            this.messageElement,
            this.transportElement
        });
    }

    public override string Scheme
    {
        get { return this.transportElement.Scheme; }
    }
}

Next we need to create CustomChannelBindingElement that inherits from TransportBindingElement class. The implementation is quite simple. The scheme may be any prefix you like “http” or “myHttp”. In build channel and listener methods we are simply creating new instances of CustomChannelFactory and CustomChannelListener objects.

 public class CustomChannelBindingElement : TransportBindingElement
 {
    public CustomChannelBindingElement() { }

    public CustomChannelBindingElement(CustomChannelBindingElement clone) { }

    public override string Scheme
    {
        get { return "myprotocol"; }
    }

    public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
    {
        return typeof(TChannel) == typeof(IRequestChannel);
    }

    public override bool CanBuildChannelListener<TChannel>(BindingContext context)
    {
        return typeof(TChannel) == typeof(IReplyChannel);
    }

    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (!CanBuildChannelFactory<TChannel>(context))
        {
            throw new ArgumentException(String.Format("Unsupported channel type: {0}.", typeof(TChannel).Name));
        }
        return (IChannelFactory<TChannel>)(object)new CustomChannelFactory(this, context);
    }

    public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        if (!CanBuildChannelListener<TChannel>(context))
        {
            throw new ArgumentException(String.Format("Unsupported channel type: {0}.", typeof(TChannel).Name));
        }
        return (IChannelListener<TChannel>)(object)new CustomChannelListener(this, context);
    }

    public override BindingElement Clone()
    {
        return new CustomChannelBindingElement(this);
    }
}

Next, we need to create CustomChannelFactory class passing in transportElement and BindingContext to the constructor. Please note the context.BindingParameters.Remove method removes binding element before the validation takes place in async calls. Then, based on MessageEncodingBindingElement we create encoder factory.

 class CustomChannelFactory : ChannelFactoryBase<IRequestChannel>
 {
    readonly MessageEncoderFactory encoderFactory;

    public CustomChannelFactory(CustomChannelBindingElement transportElement, BindingContext context)
        : base(context.Binding)
    {
        var messageElement = context.BindingParameters.Remove<MessageEncodingBindingElement>();
        this.encoderFactory = messageElement.CreateMessageEncoderFactory();
    }

    protected override IRequestChannel OnCreateChannel(EndpointAddress address, Uri via)
    {
        return new CustomRequestChannel(this, encoderFactory, address, via);
    }

    protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override void OnEndOpen(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override void OnOpen(TimeSpan timeout)
    {

    }
}

Next step is to create CustomChannelListener inheriting from ChannelListenerBase. In this class I have also implemented some methods needed when working with async calls.

 class CustomChannelListener : ChannelListenerBase<IReplyChannel>
 {
    #region privates
    readonly Uri uri;
    readonly MessageEncoderFactory encoderFactory;
    CustomReplyChannel innerChannel;
    delegate IReplyChannel AsyncOnAcceptCaller(TimeSpan timeout);
    AsyncOnAcceptCaller asyncOnAcceptCaller; 
    #endregion

    public CustomChannelListener(CustomChannelBindingElement transportElement, BindingContext context)
        : base(context.Binding)
    {
        this.uri = new Uri(context.ListenUriBaseAddress, context.ListenUriRelativeAddress);
        var messageElement = context.BindingParameters.Remove<MessageEncodingBindingElement>();

        this.encoderFactory = messageElement.CreateMessageEncoderFactory();
        this.asyncOnAcceptCaller = new AsyncOnAcceptCaller(this.OnAcceptChannel);
    }

    protected override IReplyChannel OnAcceptChannel(TimeSpan timeout)
    {
        var address = new EndpointAddress(this.Uri);
        innerChannel = new CustomReplyChannel(this, encoderFactory, address);

        return innerChannel;
    }

    protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
    {
        return asyncOnAcceptCaller.BeginInvoke(timeout, callback, state);
    }

    protected override IReplyChannel OnEndAcceptChannel(IAsyncResult result)
    {
        return asyncOnAcceptCaller.EndInvoke(result);
    }

    protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override bool OnEndWaitForChannel(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override bool OnWaitForChannel(TimeSpan timeout)
    {
        throw new NotImplementedException();
    }

    public override Uri Uri
    {
        get { return this.uri; }
    }

    protected override void OnAbort()
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override void OnClose(TimeSpan timeout)
    {
        throw new NotImplementedException();
    }

    protected override void OnEndClose(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override void OnEndOpen(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override void OnOpen(TimeSpan timeout)
    {

    }
}

Let’s create our channelbase now. Please note that you need to pass in ChannelManagerBase to the constructor. Our read and write message methods will simply create new messages with the data as a string passed in from the request and reply channels. Depending on your requirements you may want to implement your own mechanism of creating messages eg. read from files etc.

 public abstract class CustomChannelBase : ChannelBase
 {
    #region privates
    readonly EndpointAddress address;
    readonly MessageEncoder encoder;
    readonly ChannelManagerBase manager;

    #endregion

    public EndpointAddress RemoteAddress
    {
        get { return this.address; }
    }

    public CustomChannelBase(ChannelManagerBase manager, MessageEncoderFactory encoderFactory, EndpointAddress addres)
        : base(manager)
    {
        this.address = address;
        this.manager = manager;
        this.encoder = encoderFactory.CreateSessionEncoder();
    }

    public Message ReadMessage(string data)
    {
        return Message.CreateMessage(MessageVersion.Default, "fault", data);
    }

    public Message WriteMessage(string data)
    {
        return Message.CreateMessage(MessageVersion.Default, "fault", data);
    }

    protected override void OnAbort()
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override void OnClose(TimeSpan timeout)
    {

    }

    protected override void OnEndClose(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override void OnEndOpen(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override void OnOpen(TimeSpan timeout)
    {

    }
}

Finally, we can create our reply channel that will be used to reply to incoming requests. In this example we will use tcpListener to communicate with the client. In this case WCF service will serve as a wrapper for the underlying TCP communication. In the method WaitForRequest we are simply waiting for incoming message and returning true. After that, the method IReplyChannel.ReceiveRequest is triggered. In that method we simply read the message from the TCP client passing it to the request context.

 public class CustomReplyChannel : CustomChannelBase, IReplyChannel
{
    #region privates
    readonly EndpointAddress localAddress;
    readonly object readLock;
    readonly MessageEncoderFactory encoderFactory;
    delegate IReplyChannel AsyncOnAcceptCaller(TimeSpan timeout);
    AsyncOnAcceptCaller asyncOnAcceptCaller;

    TcpListener tcpListener;
    TcpClient tcpClient;
    #endregion

    public EndpointAddress LocalAddress
    {
        get { return this.localAddress; }
    }

    public CustomReplyChannel(ChannelManagerBase manager, MessageEncoderFactory encoderFactory, EndpointAddress address)
        : base(manager, encoderFactory, address)
    {
        this.encoderFactory = encoderFactory;
        this.localAddress = address;
        this.asyncOnAcceptCaller = new AsyncOnAcceptCaller(this.OnAcceptChannel);

        //start listener
        if (this.tcpListener == null)
        {
            this.tcpListener = new TcpListener(address.Uri.Port);
        }

        tcpListener.Start();
    }

    protected IReplyChannel OnAcceptChannel(TimeSpan timeout)
    {
        var address = new EndpointAddress(this.localAddress.Uri);

        return new CustomReplyChannel(this.Manager, this.encoderFactory, address);
    }

    #region IReplyChannel Members

    IAsyncResult IReplyChannel.BeginReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    IAsyncResult IReplyChannel.BeginReceiveRequest(AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    IAsyncResult IReplyChannel.BeginTryReceiveRequest(TimeSpan timeout, AsyncCallback callback, object state)
    {
        return asyncOnAcceptCaller.BeginInvoke(timeout, callback, state);
    }

    IAsyncResult IReplyChannel.BeginWaitForRequest(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    RequestContext IReplyChannel.EndReceiveRequest(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    bool IReplyChannel.EndTryReceiveRequest(IAsyncResult result, out RequestContext context)
    {
        context = ((IReplyChannel)this).ReceiveRequest(DefaultReceiveTimeout);
        return true;
    }

    bool IReplyChannel.EndWaitForRequest(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    EndpointAddress IReplyChannel.LocalAddress
    {
        get { throw new NotImplementedException(); }
    }

    RequestContext IReplyChannel.ReceiveRequest(TimeSpan timeout)
    {
        ThrowIfDisposedOrNotOpen();

        try
        {
            var clientStream = tcpClient.GetStream();

            var message = new byte[4096];
            var enc = new ASCIIEncoding();

            var bytesRead = clientStream.Read(message, 0, 4096);
            var response = enc.GetString(message, 0, bytesRead);

            var buffer = enc.GetBytes("Message received");

            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();

            return new CustomRequestContext(this.ReadMessage(response), this);
        }
        catch (SocketException ex)
        {
            return new CustomRequestContext(this.ReadMessage(ex.Message), this);
        }
        catch (Exception ex)
        {
            return new CustomRequestContext(this.ReadMessage(ex.Message), this);
        }
    }

    RequestContext IReplyChannel.ReceiveRequest()
    {
        throw new NotImplementedException();
    }

    bool IReplyChannel.TryReceiveRequest(TimeSpan timeout, out RequestContext context)
    {
        throw new NotImplementedException();
    }

    bool IReplyChannel.WaitForRequest(TimeSpan timeout)
    {
        ThrowIfDisposedOrNotOpen();

        //blocks until a client has sent a message
        this.tcpClient = tcpListener.AcceptTcpClient();

        return true;
    }

    #endregion

    #region IChannel Members

    T IChannel.GetProperty<T>()
    {
        T channel = encoderFactory.Encoder.GetProperty<T>();
        if (channel != null)
        {
            return channel;
        }

        if (typeof(T) == typeof(MessageVersion))
        {
            return (T)(object)encoderFactory.Encoder.MessageVersion;
        }

        return base.GetProperty<T>();
    }

    #endregion

    #region ICommunicationObject Members

    void ICommunicationObject.Abort()
    {
        throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    void ICommunicationObject.Close()
    {
        throw new NotImplementedException();
    }

    event EventHandler ICommunicationObject.Closed
    {
        add { throw new NotImplementedException(); }
        remove { throw new NotImplementedException(); }
    }

    event EventHandler ICommunicationObject.Closing
    {
        add { throw new NotImplementedException(); }
        remove { throw new NotImplementedException(); }
    }

    void ICommunicationObject.EndClose(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    void ICommunicationObject.EndOpen(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    event EventHandler ICommunicationObject.Faulted
    {
        add { throw new NotImplementedException(); }
        remove { throw new NotImplementedException(); }
    }

    event EventHandler ICommunicationObject.Opened
    {
        add { throw new NotImplementedException(); }
        remove { throw new NotImplementedException(); }
    }

    event EventHandler ICommunicationObject.Opening
    {
        add { throw new NotImplementedException(); }
        remove { throw new NotImplementedException(); }
    }

    CommunicationState ICommunicationObject.State
    {
        get { throw new NotImplementedException(); }
    }

    #endregion
}

The request channel is quite straightforward as it simply sends messages using TCP client. We are also reading reply to confirm that our message has been delivered.

 public class CustomRequestChannel : CustomChannelBase, IRequestChannel
 {
    readonly Uri via;

    public Uri Via
    {
        get { return this.via; }
    }

    public CustomRequestChannel(ChannelManagerBase manager, MessageEncoderFactory encoderFactory, EndpointAddress address, Uri via)
        : base(manager, encoderFactory, address)
    {
        this.via = via;
    }

    public Message Request(Message message, TimeSpan timeout)
    {
        ThrowIfDisposedOrNotOpen();

        try
        {
            using (var tcpClient = new TcpClient(this.Via.Host, this.Via.Port))
            {
                var clientStream = tcpClient.GetStream();

                tcpClient.ReceiveTimeout = timeout.Seconds;
                tcpClient.SendTimeout = timeout.Seconds;

                var msg = new byte[4096];
                var encoder = new ASCIIEncoding();

                //get string value from message body
                var messageBody = message.GetBody<string>();
                var buffer = encoder.GetBytes(messageBody);

                //send message
                clientStream.Write(buffer, 0, buffer.Length);
                clientStream.Flush();

                //read reply
                var bytesRead = clientStream.Read(msg, 0, 4096);
                var response = encoder.GetString(msg, 0, bytesRead);

                return this.ReadMessage(response);
            }
        }
        catch (SocketException ex)
        {
            return this.ReadMessage(ex.Message);
        }
        catch (Exception ex)
        {
            return this.ReadMessage(ex.Message);
        }
    }

    public Message Request(Message message)
    {
        return this.Request(message, DefaultReceiveTimeout);
    }

    public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    public Message EndRequest(IAsyncResult result)
    {
        throw new NotImplementedException();
    }
}

Finally we can test our server service

 static void StartService()
    {
        var binding = new WCFCustomChannelLib.CustomBinding();
        var uri = new Uri("myProtocol://localhost:81/x");

        var listener = binding.BuildChannelListener<IReplyChannel>(uri, new BindingParameterCollection());

        listener.Open(TimeSpan.FromSeconds(5));

        Console.Write("Creating channel...");
        Console.Write(Environment.NewLine);

        var channel = listener.AcceptChannel(TimeSpan.FromSeconds(5));
        channel.Open(TimeSpan.FromSeconds(5));

        Console.Write("Waiting for request...");
        Console.Write(Environment.NewLine);

        while (channel.WaitForRequest(TimeSpan.FromMinutes(1)))
        {
            using (var context = channel.ReceiveRequest(TimeSpan.FromSeconds(5)))
            {
                using (var message = context.RequestMessage)
                {
                    Console.WriteLine("Received message: {0}", message.GetBody<string>());
                    Console.Write(Environment.NewLine);

                    var replyMessage = Message.CreateMessage(MessageVersion.Default, "fault", "Message received");

                    context.Reply(replyMessage, TimeSpan.FromSeconds(5));
                }
            }
            Console.Write("Waiting for request...");
            Console.Write(Environment.NewLine + "------------------" + Environment.NewLine);
        }

        Console.WriteLine("terminated");
        channel.Close(TimeSpan.FromSeconds(5));
    }
}

And the client service

 static void Main(string[] args)
    {
        Console.Write("Creating factory...");

        var binding = new WCFCustomChannelLib.CustomBinding();

        var factory = binding.BuildChannelFactory<IRequestChannel>();
        factory.Open(TimeSpan.FromSeconds(5));

        Console.Write(Environment.NewLine);
        Console.Write("Creating channel...");

        var uri = new Uri("myProtocol://localhost:81/x");
        var channel = factory.CreateChannel(new EndpointAddress(uri));

        Console.Write(Environment.NewLine);
        Console.Write("Opening channel...");
        Console.Write(Environment.NewLine);

        channel.Open(TimeSpan.FromSeconds(5));
           
        while (true)
        {
            Console.Write("Enter message text: ");
            Console.Write(Environment.NewLine + "------------------" + Environment.NewLine);

            var messageText = Console.ReadLine();
            if (messageText == null) { break; }

            Console.Write("Sending request...");
            Console.Write(Environment.NewLine);

            var requestMessage = Message.CreateMessage(MessageVersion.Default, "fault", messageText);
            var replyMessage = channel.Request(requestMessage, TimeSpan.FromSeconds(5));
               
            using (replyMessage)
            {
                if (replyMessage != null)
                {
                    Console.WriteLine("Reply: {0}", replyMessage.GetBody<string>());
                }
            }
        }

        channel.Close(TimeSpan.FromSeconds(5));
        factory.Close();
    }
}

I have included working sample that you can download and test on your own. Feel free to play with it 🙂

WCFCustomChannel

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...