Friday, June 17, 2016

C# run jobs (Task) on multiple threads, return upon completion of the first one

class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}

static async void Test()
{
int result = await TaskTest();
Console.WriteLine(result);
}
static async Task<int> TaskTest()
{
Task<int> task1 = new Task<int>(() => { Thread.Sleep(1000); return 1; });
Task<int> task2 = new Task<int>(() => { Thread.Sleep(50); return 2; });
task1.Start();
task2.Start();
var multiTaskResult = await Task.WhenAny(task1, task2);
return multiTaskResult.Result;
}
}

Wednesday, June 8, 2016

ASP.Net MVC Multiple submit buttons on a Form

// Form markup
<input name="submit" type="submit" value="Download" class="btn btn-primary" />
<input name="submit" type="submit" value="Display" class="btn btn-primary" />

// Controller processor
public ActionResult SomeReport(SomeModel model,string submit)
{
if (submit == "Display")
{
return View(model);
}
else if (submit == "Download")
{
return DownloadReport(model);
}
}

ActionResult DownloadRevenueReport(RevenueReportListModel model)
{
return File(Encoding.UTF8.GetBytes(GetTextData(model)),
"application/text", "Report.csv");
}

Tuesday, June 7, 2016

ASP.Net MVC HTTP GET Controller Action Processing Pattern for Reports or Searches

Scenario


  • Select a month
  • Days of month are displayed on the same view


Controller

[HttpGet]
public ActionResult Index(ReportPageSelectionModel model, string msg)
{
String preSelectedMonth = null;
try
{
ViewBag.Msg = msg;

if (model.DisplayResults == false)
{
model.MonthList = ReportPageModelFactory.GetMonthList();
return View(model);
}
else
{
preSelectedMonth = model.SelectedMonth;
model.MonthList = ReportPageModelFactory.GetMonthList();
ReportPageResultModel resultModel =
ReportPageModelFactory.GetResultsModel(model.SelectedMonth);
ViewBag.Result = resultModel;
return View(model);
}
}
catch (Exception ex)
{
return RedirectToAction("Index", new { Msg = GetInnerMostException(ex).Message, SelectedMonth = preSelectedMonth });
}
}

Exception GetInnerMostException(Exception ex)
{
if (ex.InnerException == null)
return ex;
return GetInnerMostException(ex.InnerException);
}

View

@model ReportPageSelectionModel
@using ReportPage.Models

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

<div>
    <h3>Criteria Selection</h3>

    @using (Html.BeginForm("Index", "Home", FormMethod.Get))
    {
        <div class="form-horizontal">

            @Html.ValidationSummary(true, "", new { @class = "text-danger" })

            <div class="form-group">
                <div class="control-label col-md-3"><strong>@Html.LabelFor(model => model.SelectedMonth)</strong></div>
                <div class="col-md-9">
                    <div class="form-control-static">
                        @Html.DropDownListFor(model => model.SelectedMonth, Model.MonthList)
                    </div>
                </div>
            </div>

            <div class="form-group">
                <div class="col-md-offset-4 col-md-8">
                    <input type="hidden" name="DisplayResults" value="true" />
                    @Html.ActionLink("Reset", "Index", null, new { @class = "btn btn-default" })
                    <input type="submit" value="Go" class="btn btn-primary" />
                </div>
            </div>
        </div>
    }

    @if (@ViewBag.Msg != null)
    {
        <div class="alert alert-danger">
            <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
            <strong>@ViewBag.Msg</strong>
        </div>
    }

</div>

<hr />

@{
    var result = @ViewBag.Result as ReportPageResultModel;
}

@if (result != null)
{
    <div>
        <h3>Result</h3>
        @foreach(var day in result.Result)
        {
            <p>@day</p>
        }
    </div>
}

@section scripts
{
    <script>
        $(function () {
            $("#SelectedMonth").change(function () {
                $("form").submit();
            });
        });
    </script>
}

Models

public class ReportPageSelectionModel
{
[DisplayName("Month")]
[Required]
public string SelectedMonth { get; set; }
public List<SelectListItem> MonthList { get; set; }
public bool DisplayResults { get; set; }
}

public class ReportPageResultModel
{
public DateTime SelectedMonth { get; set; }
public List<string> Result { get; set; }
}


Model Factories

static public class ReportPageModelFactory
{
static public ReportPageResultModel GetResultsModel(string selectedMonthStr)
{
var model = new ReportPageResultModel();
model.SelectedMonth = DateTime.Parse(selectedMonthStr);
model.Result = new List<string>();
for (int i=1; i<= DateTime.DaysInMonth(model.SelectedMonth.Year, model.SelectedMonth.Month); i++)
{
DateTime day = new DateTime(model.SelectedMonth.Year, model.SelectedMonth.Month, i);
model.Result.Add(day.ToString("dddd MMMM d, yyyy"));
}
return model;
}

static public List<SelectListItem> GetMonthList()
{
var monthList = new List<SelectListItem>();
for (int i = 1; i <= 12; i++)
{
DateTime month = new DateTime(DateTime.UtcNow.Year, i, 1);
monthList.Add(
new SelectListItem()
{
Text = String.Format("{0}  {1}", month.Year, month.ToString("MMMM")),
Value = month.ToString()
});
}
return monthList;
}
}

Thursday, May 12, 2016

ASP.Net MVC HTTP POST Controller Action Processing Pattern

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add(SomeModel model, FormCollection collection)
{
try
{
if (!ModelState.IsValid)
{
// re-populate lookup (Select) lists, if necessary
//   Helper.SetLookupLists(model);
return View("Add", model);
}
else
{
// Helper class to process posted data
var process = new AddProcess(this.HttpContext);
process.Execute(model);  // throws exception if there is a problem
// Success - Redirect
return RedirectToAction("AddConfirmation", new
{
Id = model.Id,
Name = model.Name
});
}
}
catch (Exception ex)
{
// re-populate lookup (Select) lists, if necessary
//   Helper.SetLookupLists(model);
ModelState.AddModelError("", ex.Message);
return View("Add", model);
}
}

Thursday, April 28, 2016

Scrolling to an element on a page using jQuery

// id is the id of an HTML element (without the # prefix)
//   the element with the id must not have display:none style
//   (otherwise .offset() will be null)
// fixedNavBarHeight is height of the fixed nav bar at the top
// Call this onReady handler (that will ensure target element has been loaded before the call)
function scrollTo(id, fixedNavBarHeight) {
            $('html,body').animate({ scrollTop: $("#" + id).offset().top - fixedNavBarHeight }, 'fast');
        }

Wednesday, April 27, 2016

AWS Lambda and API Gateway

Lambda - What is it?

A unit of code with a single entry point that you upload to AWS managed compute resources and execute on-demand or in response to events (e.g. object loaded to S3, data arrived in a Kinesis Stream, etc.).

API Gateway - What is it?

API Gateway exposes Internet-accessible endpoints. The API requests received can be routed to another Internet endpoint (HTTP Proxy), to Lambda, and to another compute resource on EC2.
  • Mock integration allows API to return canned responses to aid in development.
  • AWS Congnito can be used to manage user-based access control.
  • Variables can be defined at API level when deployed to a given "stage". These variables can be passed to the back end (which can use to look up configuration or change behavior).
  • Request/Response templates can be used to filter/format data to pass to the back end.
  • Full control of HTTP code and response returned.
  • Swagger import/export to specify/source control the API definition.
  • API Gateway calls can be throttled via config to protect back-end resources.
  • Passes data as JSON object to Lambda. Can use mapping template to transform. 
  • Use template to transform (uses Velocity Template Language). Better to use capabilities in gateway than in lambda (timed execution).
  • Url path parameters can also be passed to Lambda. "apiname/{action}" will be passed as {"action" : "getcustomers"} for apiname/getcustomers
  • Information from HTTP request can be used in creating a RESTful API. URL parameter values can used to identify the resource (e.g. Customer, Product) and the operation to perform can deduced from HTTP method.
  • API Gateway can create client-side SDK (JavaScript, iOS, and Android)

Lambda Available Resources

  • Memory - Specify 128 MB to 1.5GB, affects performance of CPU and Network
  • Compute time - Specify when to timeout the function. Up to 300 seconds.
  • Temporary storage: 500MB as /tmp
  • The assigned IAM role dictates the permissions of Lambda to other AWS resources

Lambda Implementation

  • Can be coded in Java, NodeJS, or Python.
  • The entry point function retrieves the event (data associated with event such as S3 event or custom event; all in JSON format) and context (memory limit in MB, functionName, functionVersion, functionARN, requestId, streamName, LogGroupName, clientContext, identity, remaining time in milliseconds, logger).
  • IAM assigned to Lambda dictates which resources Lambda can use (use least privilege).
  • Function alias is useful in creating another level of indirection, for example, when mapping API Gateway to Lambda functions.
  • External configuration can be stored in DynamoDB (can use information provided in Context such as API Gateway stage passed to lambda function as the key to the configuration information).
  • The Context passed to function has a logger that writes to Cloudwatch logs.
  • Can have one big lambda function that performs a group of related tasks.

Trace Data Passed to Lambda

'use strict';
console.log('Loading function');

exports.handler = (event, context, callback) => {
    var eventJson = JSON.stringify(event, null, 2);
    var contextJson = JSON.stringify(context, null, 2);
    console.log('Event:', eventJson);
    console.log('Context:', contextJson);
    // return received data
    // callback(Error error, Object result);
    callback(null, {event: event, context: context});
};

Minimum Permission Required for IAM

// must be able to write to CloudWatch in order log error messages
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    }
  ]
}

Lambda Deployment

  • AWS takes of deployment and transition to new code uploaded.
  • Automate it (CLI, Jenkins build, CloudFormation)
  • Functions can be versioned - the calls to lambda can reference a particular version. New version can be loaded but the deployment will continue to use the referenced version. In code, reference the lambda with the desired version. Without version specification, latest version is executed.
  • Can create alias for a version of lambda. Can repoint the alias to a different version at any time. In code, reference the lambda by its alias (functionname:alias).

Lambda Invocation

  • Lambda can be invoke in response to AWS events such S3, SNS, CloudWatch, and Kinesis.
  • An API Gateway endpoint can be placed in front of a Lambda function. The function can then be invoked from the Internet. 
  • Lambda can be invoked using the AWS SDK. For example, to save processing time on an EC2 instance, call lambda to do the work.
  • Use naming conventions to tie Lambda function, API Gateway Resource, IAM role name, API stage names (helps in automation).
  • Instances may be reused on subsequent calls. Large Java functions may take sometime to load up.
  • There is a warm-up time for a lambda. Invoke lambda every 9 minutes to keep the warmed instance alive. Set a generous timeout. First invocations may take a while. Keep the function size small so it loads faster.
  • IP of the lambda function is indeterminate.

Example of a generic Lambda Custom Event

{
   "metadata" : {},   // invokers IP  address, hostname, user agent, action to perform etc.
   "data" : {}
}

Monitoring

Use CloudWatch alarm to monitor failed, and throttled invocations. Also, alert above-average execution times.

Sunday, April 24, 2016

C# DateTime vs DateTimeOffset

DateTimeOffset is similar to DateTime except it allows explicit specification of "offset" of the date time from UTC.

CODE

DateTime dateTime = new DateTime(2016, 4, 22, 0, 0, 0, DateTimeKind.Unspecified);            Debug.WriteLine(DateTimeKind.Unspecified.ToString() + ": " + dateTime.ToString("o"));
            // json serialize applies ToString("o")
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTime));

            DateTime dateTimeUTC = new DateTime(2016, 4, 22, 0, 0, 0, DateTimeKind.Utc);
            Debug.WriteLine(DateTimeKind.Utc.ToString() + ": " + dateTimeUTC.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeUTC));

            DateTime dateTimeLocal = new DateTime(2016, 4, 22, 0, 0, 0, DateTimeKind.Local);
            Debug.WriteLine(DateTimeKind.Local.ToString() + ": " + dateTimeLocal.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeLocal));

            DateTime dateTimeLocalNow = DateTime.Now;
            Debug.WriteLine(dateTimeLocalNow.Kind.ToString() + ": " + dateTimeLocalNow.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeLocalNow));

            DateTimeOffset dateTimeOffsetNoOffset = new DateTimeOffset(2016, 4, 22, 0, 0, 0, new TimeSpan());
            Debug.WriteLine("No offset: " + dateTimeOffsetNoOffset.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeOffsetNoOffset));

            DateTimeOffset dateTimeOffsetUTC = new DateTimeOffset(2016, 4, 22, 0, 0, 0, new TimeSpan(0, 0, 0));
            Debug.WriteLine("Zero offset: " + dateTimeOffsetUTC.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeOffsetUTC));

            DateTimeOffset dateTimeOffsetSpecified = new DateTimeOffset(2016, 4, 22, 0, 0, 0, new TimeSpan(-7, 0, 0));
            Debug.WriteLine("Specified offset: " + dateTimeOffsetSpecified.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeOffsetSpecified));

            DateTimeOffset dateTimeOffsetLocalNow = DateTimeOffset.Now;
            Debug.WriteLine("Local offset: " + dateTimeOffsetLocalNow.ToString("o"));
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeOffsetLocalNow));

            DateTimeOffset dateTimeOffsetUtcNow = DateTimeOffset.UtcNow;
            Debug.WriteLine("Utc offset: " + dateTimeOffsetUtcNow.ToString("o"));
            // json serialize applies ToString("o")
            Debug.WriteLine("Json Serialize: " + Newtonsoft.Json.JsonConvert.SerializeObject(dateTimeOffsetUtcNow));

OUTPUT

Unspecified: 2016-04-22T00:00:00.0000000
Json Serialize: "2016-04-22T00:00:00"

Utc: 2016-04-22T00:00:00.0000000Z
Json Serialize: "2016-04-22T00:00:00Z"

Local: 2016-04-22T00:00:00.0000000-07:00
Json Serialize: "2016-04-22T00:00:00-07:00"

Local: 2016-04-24T12:18:09.1892907-07:00
Json Serialize: "2016-04-24T12:18:09.1892907-07:00"

No offset: 2016-04-22T00:00:00.0000000+00:00
Json Serialize: "2016-04-22T00:00:00+00:00"

Zero offset: 2016-04-22T00:00:00.0000000+00:00
Json Serialize: "2016-04-22T00:00:00+00:00"

Specified offset: 2016-04-22T00:00:00.0000000-07:00
Json Serialize: "2016-04-22T00:00:00-07:00"

Local offset: 2016-04-24T12:18:09.1892907-07:00
Json Serialize: "2016-04-24T12:18:09.1892907-07:00"

Utc offset: 2016-04-24T19:18:09.1892907+00:00
Json Serialize: "2016-04-24T19:18:09.1892907+00:00"