.Net Core porting challenges


I was recently porting a .Net 4.6.2 C# Scheduler app to .Net core 1.0.1 Cent OS.7 64 bit machine in Azure and I’ve learned a good lesson especially with HttpClient API. I will be mainly focusing some of the issues I’ve encountered with .Net Core HttpClient library in Linux platform in this article.

As I mentioned above, Initially this app was targeting to .NetFramework Windows server and later I ported to Linux. Once after the app has been deployed to Azure Linux I was getting the following issues.

NOTE : Most of the issues I am talking here did not show up in Windows platform (Windows 10, Windows Server 2012 R2).

1) AppDomain & Unhanded exceptions

AppDomain.CurrentDomain.UnhandledException += UnhandledException;

The above one liner code for handling UnhandledException exception doesn’t work .Netcore 1.0 or 1.1. AppDomain class supports started from .NET Framework 1.1, however this was removed in .NetCore due to performance issue and other reasons here here and here which is fine I am totally agree the point. However, it would be great if they have alternate solution for handling Unhanded exception. Hoping this would get added in .Net Core 2.0 release.

2) ServicePointManager 

In .Net core there is no ServicePointManager class instead use base HttpHandler class and If you are on Windows use WinHttpHanlder and non windows platform make sure to use HttpClientHandler to call the method and properties like below.

 var handler = new HttpClientHandler
 {
     AllowAutoRedirect = false;
     AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
 };
 handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;


3) PlatformNotSupportedException for ServerCertificateCustomValidationCallback

One of the common error you might encounter while using .Net core or Net standard in non windows platform is PlatformNotSupportedException. Hopefully they are going to address this in 2.0 versions. ServerCertificateCustomValidationCallback works fine in windows. However, by default this won’t work in Linux and MAC OS unless the libcurl library point to OpenSSL. I have opened a github issue https://github.com/dotnet/corefx/issues/17045 and .Net corefx team was provided some solution to address the issue. Unfortunately there are no documentation explains these kind of issues.

The libcurl library in use (7.29.0) and its 
SSL backend ("NSS/3.19.1 Basic ECC") do not support custom handling of certificates. 
A libcurl built with OpenSSL is required.


4) System.Net.Http.HttpRequestException: The server returned an invalid or unrecognized response.
I have encountered this exception lately and I opened a new github issue https://github.com/dotnet/corefx/issues/17558 and the corefx team planing to address this issue in 2.0.

Summary

Porting to .net core app is really great experience and good learning curve for the new platform. I would definitely recommend to check .NET Portability Analyzer to find out how much libraries and APIs are supporting to .Net Core. Also, follow github issue tracker for .Net Corefx repository.

References / Tools –
https://apisof.net/
http://packagesearch.azurewebsites.net/
https://marketplace.visualstudio.com/items?itemName=ConnieYau.NETPortabilityAnalyzer

Convert C# camel case json property to pascal case


I am pretty sure that many of them faced the same scenario where you would end up copy/paste some json data from a third party API sites And using one of the tools like Visual Studio IDE or json2csharp.com to generate POCO classes. However, these tools will not generate pascal case property’s automatically And if you want to maintain the coding standard you have to change the property’s to pascal case manually.

For example : Default camel case property’s generated based on Visual Studio IDE

public class Refunds
{
   public string @object { get; set; }
   public bool has_more { get; set; }
}

Since I had to convert many json property’s to pascal casing which is a tedious task so I have developed an app to convert camel case csharp json property to pascal case. http://deepumi.com/jsonproperty2TitleCase/

jsonProperty2TitleCase Core Features:

  • Convert property to Title case which is Pascal case
  • Add NewtonsoftJson property attribute to add the actual json property name for deserializing purpose.
  • Also, handles nested classes hierarchies.
  • Beautify your csharp code  (Remove all extra white spaces)

jsonProperty2TitleCase Output:

using Newtonsoft.Json;

public class Refunds
{
    [JsonProperty("object")]
    public string Object { get; set; }

    [JsonProperty("has_more")]
    public bool HasMore { get; set; }
}

http://deepumi.com/jsonproperty2TitleCase/

jsonproperty

Please feel free to give this a try and let me know your thoughts and suggestions.

http://deepumi.com/jsonproperty2TitleCase/

Asp.Net MVC5 Authorization


The sample application will walk you through how to create Claim based Authorization Middleware with Owin and Katana libraries using .Net 4.5.2 framework.

Github source – https://github.com/deepumi/AspNetMVC5Authorization

Step1

  • File -> New Project -> Asp.Net Web Application -> Asp.Net 4.5.2 Templates
  • Choose empty MVC project template

Step2

  • Install following nuget packages
    • Microsoft.AspNet.Identity.Core
    • Microsoft.AspNet.Identity.Owin
    • Microsoft.Owin
    • Microsoft.Owin.Host.SystemWeb
    • Microsoft.Owin.Security
    • Microsoft.Owin.Security.Cookies
    • Microsoft.Owin.Security.OAuth
    • Owin

Step3

  • Create a Owin Startup class and decorate with assembly attribute OwinStartup.
    using Microsoft.AspNet.Identity;
    using Microsoft.Owin;
    using Microsoft.Owin.Security.Cookies;
    using Owin;
    
    [assembly: OwinStartup(typeof(AspNetMVC5Authorization.Startup))]
    
    namespace AspNetMVC5Authorization
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                ConfigureAuthentication(app);
            }
    
            private void ConfigureAuthentication(IAppBuilder app)
            {
                app.UseCookieAuthentication(new CookieAuthenticationOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                    LoginPath = new PathString("/Auth/Login"), //sign page
                    CookieName = "AuthCookie",
                    CookieHttpOnly = true,
                    ExpireTimeSpan = System.TimeSpan.FromHours(1),
                    LogoutPath = new PathString("/Auth/Signout"), //sign out page
                    ReturnUrlParameter = "ReturnUrl",
                    CookieSecure = CookieSecureOption.SameAsRequest, //Use CookieSecureOption.Always if you intend to serve cookie in SSL/TLS (Https)
                    SlidingExpiration = true,
                });
            }
        }
    }
    

    Step4

  • Create list of claims for the specific user with roles and other information
    internal class AuthenticationHelper
    {
       internal static List<Claim> CreateClaim(UserSessionModel userSessionModel,params string[] roles) //Single or multiple roles
       {
          var claims = new List<Claim>
          {
              new Claim(ClaimTypes.NameIdentifier, userSessionModel.UserId.ToString()),  //User ideitifer
              new Claim(ClaimTypes.Name, userSessionModel.DisplayName),  //Username
              new Claim(Constants.UserSession, userSessionModel.ToJson()) //Custom entity with user info
          };
    
          foreach (var role in roles) //custom roles goes here
          {
              claims.Add(new Claim(ClaimTypes.Role, role, ClaimValueTypes.String, Constants.Issuer));
          }
          return claims;
       }
    }

Step5

  • Create Authentication controller and process of the rest of the Authentication
    using Microsoft.AspNet.Identity;
    using Microsoft.Owin.Security;
    using System.Security.Claims;

    public class AuthController : BaseController
    {
      private IAuthenticationManager AuthenticationManager
      {
          get { return HttpContext.GetOwinContext().Authentication; }
      }

      [HttpPost,ValidateAntiForgeryToken]
      public ActionResult SignIn(SignInViewModel vm,string returnUrl = default(string))
      {
          try
          {
              if (!ModelState.IsValid)
              {
                  vm.ErrorMessage = "Email address and Password are required fields";
                  return View(vm);
              }

              var userSession = Authenticate(vm); // Validate authentication from db or other source.

              if (userSession != null) //create claim with user info.
              {
                  var identity = new ClaimsIdentity(AuthenticationHelper.CreateClaim(userSession, 
                                                      Helpers.Constants.UserRoles.Admin, 
                                                      Helpers.Constants.UserRoles.User),
                                                      DefaultAuthenticationTypes.ApplicationCookie
                                                      );
                  AuthenticationManager.SignIn(new AuthenticationProperties()
                  {
                      AllowRefresh = true,
                      IsPersistent = true,
                      ExpiresUtc = DateTime.UtcNow.AddHours(1)
                  }, identity);

                  if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl)) return Redirect(returnUrl);

                  return RedirectToAction("index", "home");
              }
          }
          catch (AuthenticationException e)
          {
              vm.ErrorMessage = e.Message;
          }
          return View(vm);
      }

      private UserSessionModel Authenticate(SignInViewModel vm)
      {
          if (vm.Email != "email@email.com" || vm.Password != "password") throw new AuthenticationException("Login failed. Incorrect email address or password");

          return new UserSessionModel
          {
              UserId = Guid.NewGuid(),
              DisplayName = "Deepu Madhusoodanan"
          };
      }

      public ActionResult Signout()
      {
          AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie,DefaultAuthenticationTypes.ExternalCookie);
          return Redirect("~/");
      }
    }

Step6

  • Get the custom ojbect obect from Claim
public class BaseController : Controller
{
    protected internal UserSessionModel UserSessionModel { get; private set; }

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        var user = User as ClaimsPrincipal;
        if (user != null)
        {
            var claims = user.Claims.ToList();
            var sessionClaim = claims.FirstOrDefault(o => o.Type == Constants.UserSession);
            if (sessionClaim != null)
            {
                UserSessionModel = sessionClaim.Value.ToObject<UserSessionModel>();
            }
        }
    }
}

Step7

[Authorize(ClaimType = ClaimTypes.Role, ClaimValue = "Contributor,User")]
public ActionResult Secure()
{
    var userSessionModel = UserSessionModel; //Get custom object from Security claim.
    return View();
}

Finally Custom Authorize attribute code

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    public string ClaimType { get; set; }
    public string ClaimValue { get; set; }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        var principal = filterContext.RequestContext.HttpContext.User as ClaimsPrincipal;

        if (!principal.Identity.IsAuthenticated)
        {
            filterContext.Result = new RedirectResult("~/auth/signin");
            return;
        }

        var claimValue = ClaimValue.Split(','); //Split custom roles and validate custom cliams, issuer and vlaue.
        if (!(principal.HasClaim(x => x.Type == ClaimType && claimValue.Any(v => v == x.Value) && x.Issuer == Constants.Issuer)))
        {
            filterContext.Result = new RedirectResult("~/Unauthorize.html");
        }
    }
}

MadMimi.Net Integration


I have been playing around with Transaction Email Services for a while and came across the tool named MadMimi, A email marketing tool lets you to send email from a different server so that you can track whether the email delivered or bounce.

MadMimi already have a C# version API and its quite old. The reason I wrote my own version is to prevent less code change to my current project so that if I want to switch to a different provider in the feature it would be easy.

A typical email sending code

using (var message = new MailMessage())
{
    message.To.Add("receiver");
    message.From = new MailAddress("from");
    message.Subject = "Subject";
    message.Body = "This is test";
    using (var client = new SmtpClient())
    {
      client.Send(message);
    }
}

Using Library

  var mailer = new Mailer("username", "apikey");
  using (var message = new MailMessage())
  {
      message.From = new MailAddress("from email", "display name");
      message.To.Add("sender email");
      message.Subject = "Test";
      message.Body = "Email body";
      await mailer.SendAsync("Promotion Name", message);
  }
  var status = mailer.TrackStatus; //URL to track the email
  var mailTransactionId = mailer.TransactionId; //Transaction Id.

When you compare the above code snippets you should see additional two liner code which integrate the library (Mailer class) and that is the only change you should make your existing project and apps if you would like to integrate this.

Pros
SPF (
Sender Policy Framework) – MadMimi does not care if you have set up a SPF record in DNS. While most of the competitors, this is a mandatory requirement if you want to use your own “FROM” email address other than your DOMAIN.

Cons
1) Delivery speed – Some time the email take 5-6 minutes to deliver.
2) No SMTP relay support.
3) Attachment does not support via API.

Feel free to play around and fork it if you would like to make any change to this. The GitHub link is provided below for the sample project and library.

Repository – https://github.com/deepumi/MadMimi.Net

First Data Global Gateway Integration in Asp.Net MVC C#


I have added a new Github repository for First Data Global Gateway Integration in Asp.Net MVC.  The sample provides a rest api implementation for credit card based payment processing. Since, I have choosen Asp.Net MVC empty project template you might need to install System.Net.HttpClient library from nuget.org or install directly from visual studio.

Command – Install-Package Microsoft.Net.Http

Download Location or copy paste the url

https://github.com/deepumi/FirstDataPaymentGateway-CSharp.Net

This code was taken from the original source https://support.payeezy.com/hc/en-us/articles/204699865-C-REST-Sample-Code-For-Use-with-v12-v13

Example Usage:

 var paymentService = new PaymentService("1.50", "0515","Card holder name","4111111111111111", "23".ToString()); //customer Id for Internal purpose to track which customer made the transaction...
 var transactionResult = await paymenetService.PostAsync();
 if (transactionResult == null) return;
 if (transactionResult.TransactionApproved && !transactionResult.TransactionError && transactionResult.CustomerRef == customerId.ToString())
 {
 //transaction success....
 }
 else
 {
 //transaction failed
 var message = transactionResult.Message;
 }

Configuration Settings:
Following values needs to be changed in the web.config file.

<add key="GATEWAY_ID" value="Exact ID" />
<add key="GATEWAY_PWD" value="Password" />
<add key="TRANS_TYPE" value="00" />
<add key="HMAC_KEY" value="HMAC Key" />
<add key="KEY_ID" value="Key Id" />
<add key="GATEWAY_URL" value="https://api.demo.globalgatewaye4.firstdata.com" />

Transaction Summary file(transaction_summary.xml) from payment gateway is included in the App_Data folder.

Also, refer : https://support.payeezy.com/hc/en-us/articles/203731109-First-Data-Global-Gateway-e4-Web-Service-API-Reference-Guide

As per First Data knowledge base Note: “Code samples are provided “as is” and are not designed to be used in production.”

 

Custom Template in InfoPath 2010


I’ve been playing around with InfoPath forms this weekend  for converting paper forms to eForms (Infopath forms). One of the challenge  I have faced was maintaining the consistency of application look and feel for all the forms, since InfoPath designer doesn’t support master page layout  but it also provide an alternative solution called custom TEMPLATE control which you can convert any layout in to a .STP file and can reuse across all the forms.

Click here to Download the template

Open InfoPath Designer 2010 and select Template Parts as BLANK  and click Design Form



Design your own layout…I have created a sample STP layout and Save it as  Infopath Web Browser Template Part.


Once you  save the template as .STP file then you can add it as custom template in the template section


Click Add or Remove Custom Controls and select template part and browse your .STP file.



Once the template added successfully it will appear in the Custom control section


That’s it. Now you can reuse the new custom template / control to any new forms and existing forms.

Click here to Download the template

Hope this helps and If you have any comments, please feel free to write your feedback.

Thanks
Deepu

Silverlight Dashboard


I  have found a dashboard control from Microsoft expression website (created by infragistics.com) which  inspired me to create a  dashboard control.

Click here to watch live   |   Click here to Download

In this article I’m going to explain the types of controls used to create a interactive dashboard.

  1. Grid based layout
  2. Customer Master / Detail  list
  3. Chart control
  4. Bing Map control
  5. Custom Splash screen


Grid based layout

A simple grid  layout with 2/2 row column.

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="85"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>

Customer Master / Detail  list

For master detail scenario ListBox control is the right choice to build.. The control will list the customer name and location, once you click the detail link its going to take you to a popup window for more information.
<ListBox x:Name="lbCustomer" Style="{StaticResource ListBoxBackground}" SelectionChanged="CustomerChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>
<StackPanel>
<TextBlock Text="{Binding Name}" Foreground="YellowGreen" FontSize="14" />
<HyperlinkButton Content="Detail" Foreground="DarkRed" Name="lnkDetailCustomer" Click="CustomerDetailClick" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Chart Controls

Bar Chart

<toolkit:Chart Style="{StaticResource ChartBackground}" Name="chartCoulumn" Width="490" Height="290">
<toolkit:Chart.Series>
<toolkit:ColumnSeries SelectionChanged="ColumnSeriesChanged" Name="chart1" Title="Sales" DependentValuePath="Profit"
IndependentValuePath="Month"
AnimationSequence="Simultaneous"
IsSelectionEnabled="True">
<toolkit:ColumnSeries.DataPointStyle>
<Style TargetType="toolkit:ColumnDataPoint">
<Setter Property="Background" Value="YellowGreen"/>
<Setter Property="BorderBrush" Value="YellowGreen"/>
</Style>
</toolkit:ColumnSeries.DataPointStyle>
</toolkit:ColumnSeries>
</toolkit:Chart.Series>
</toolkit:Chart>

Pie chart

<toolkit:Chart Style="{StaticResource ChartBackground}" x:Name="chartPie" VerticalAlignment="Top" Width="340" Height="290">
<toolkit:Chart.Series>
<toolkit:PieSeries Margin="0,0,0,0" Padding="0,0,0,0" IndependentValuePath="Month"
DependentValuePath="Profit" AnimationSequence="Simultaneous"/>
</toolkit:Chart.Series>
</toolkit:Chart>

Map

<bing:Map Width="600" Grid.Row="2" Margin="2,2,2,2" HorizontalAlignment="Center" VerticalAlignment="Stretch"
CredentialsProvider="<Replace your credential here" Name="map" LogoVisibility="Collapsed" Culture="en-US" >
</bing:Map>

Click here to watch live   |   Click here to Download

Hope this helps and If you have any comments, please feel free to write your feedback.
Thanks
Deepu