KickStart - Application Initialization Framework

Application start-up helper to initialize things like an IoC container, register mapping information or run a task.

Features

  • Run tasks on application start-up
  • Extension model to add library specific start up tasks
  • Common IoC container adaptor
  • Sigleton instance of an application level IoC container

Download

The KickStart library is available on nuget.org via package name KickStart.

To install KickStart, run the following command in the Package Manager Console

PM> Install-Package KickStart

Example

This example will scan the assembly containing UserModule. Then it will find all Autofac modules and register them with Autofac. Then, all AutoMapper profiles will be registered with Automapper. Finally, it will find all classes that implement IStartupTask and run it.

Kick.Start(config => config
    .IncludeAssemblyFor<UserModule>()
    .UseAutofac()
    .UseAutoMapper()
    .UseStartupTask()
);

Extensions

  • StartupTask - Run any class that implements IStartupTask
  • Autofac - Registers all Autofac Module classes and creates the container
  • AutoMapper - Registers all AutoMapper Profile classes
  • log4net - Use log4net as a logger
  • MongoDB - Registers all BsonClassMap classes with MongoDB serialization
  • Ninject - Registers all NinjectModule classes and creates an IKernal
  • NLog - Use NLog as a logger
  • SimpleInjector - Run all ISimpleInjectorRegistration instances allowing container registration
  • Unity - Run all IUnityRegistration instances allowing container registration

StartupTask

The StartupTask extension allows running code on application start-up. To use this extension, implement the IStartupTask interface. Use the Priority property to control the order of execution.

Basic usage

Kick.Start(config => config
    .IncludeAssemblyFor<UserModule>() // where to look for tasks
    .UseStartupTask() // include startup tasks in the Kick Start        
);

Use the Common Container to resolve startup tasks

Kick.Start(config => config
    .IncludeAssemblyFor<UserModule>()
    .UseAutofac() // init Autofac or any other IoC as container
    .UseStartupTask(c => c.UseContainer()) // config to use the shared container
);

Autofac

The Autofac extension allows registration of types to be resolved. The extension also creates a default container and sets it to the Kick.Container singleton for access later.

Basic usage

Kick.Start(config => config
    .IncludeAssemblyFor<UserRepository>() // where to look for tasks
    .UseAutofac() // initialize Autofac        
);

Use with ASP.NET MVC

Kick.Start(c => c
    .IncludeAssemblyFor<UserModule>()
    .UseAutofac(a => a
        .Builder(b => b.RegisterControllers(typeof(MvcApplication).Assembly)) // register all controllers 
        .Container(r => DependencyResolver.SetResolver(new AutofacDependencyResolver(r))) // set asp.net resolver
    )
    .UseAutoMapper()
    .UseMongoDB()
    .UseStartupTask()
);

To install Autofac extension, run the following command in the Package Manager Console

PM> Install-Package KickStart.Autofac

SimpleInjector

The SimpleInjector extension allows registration of types to be resolved by running all instances of ISimpleInjectorRegistration. The extension also creates a default container and sets it to the Kick.Container singleton for access later.

Basic usage

Kick.Start(config => config
    .IncludeAssemblyFor<UserRepository>() // where to look
    .UseSimpleInjector () // initialize SimpleInjector         
);

To install SimpleInjector extension, run the following command in the Package Manager Console

PM> Install-Package KickStart.SimpleInjector

Unity

The Unity extension allows registration of types to be resolved by running all instances of IUnityRegistration. The extension also creates a default container and sets it to the Kick.Container singleton for access later.

Basic usage

Kick.Start(config => config
    .IncludeAssemblyFor<UserRepository>() // where to look
    .UseUnity () // initialize Unity         
);

To install Unity extension, run the following command in the Package Manager Console

PM> Install-Package KickStart.Unity

NLog

Use NLog as a logger for KickStart

Basic usage

Kick.Start(c => c
    .IncludeAssemblyFor<Project>()
    .UseNLog()
    .UseStartupTask()
);

Configure NLog to use ConsoleTarget

Kick.Start(c => c
    .IncludeAssemblyFor<Project>()
    .UseNLog(config =>
    {
        var consoleTarget = new ConsoleTarget();
        consoleTarget.Layout = "${time} ${level:padding=1:fixedLength=true} ${logger:shortName=true} ${message} ${exception:format=tostring}";        
        config.AddTarget("console", consoleTarget);

        var consoleRule = new LoggingRule("*", NLog.LogLevel.Trace, consoleTarget);
        config.LoggingRules.Add(consoleRule);
    })
    .UseStartupTask()
);

To install NLog extension, run the following command in the Package Manager Console

PM> Install-Package KickStart.NLog

NLog Fluent Library

Fluent API for NLog

Download

The NLog.Fluent library is available on nuget.org via package name NLog.Fluent.

To install NLog.Fluent, run the following command in the Package Manager Console

PM> Install-Package NLog.Fluent

Examples

Writing info message via fluent API.

_logger.Info()
    .Message("This is a test fluent message '{0}'.", DateTime.Now.Ticks)
    .Property("Test", "InfoWrite")
    .Write();

Writing error message.

try
{
    string text = File.ReadAllText(path);
}
catch (Exception ex)
{
    _logger.Error()
        .Message("Error reading file '{0}'.", path)
        .Exception(ex)
        .Property("Test", "ErrorWrite")
        .Write();
}

Caller Info

Use the static Log class so you don't have to include loggers in all of classes. The static Log class using .net 4.5 caller info to get the logger from the file name.

Writing info message via static Log class with fluent API.

Log.Info()
    .Message("This is a test fluent message.")
    .Property("Test", "InfoWrite")
    .Write();

Writing error message.

try
{
    string text = File.ReadAllText(path);
}
catch (Exception ex)
{
    Log.Error()
        .Message("Error reading file '{0}'.", path)
        .Exception(ex)
        .Property("Test", "ErrorWrite")
        .Write();
}

NLog MongoDB Library

Writes NLog messages to MongoDB.

Download

The NLog.Mongo library is available on nuget.org via package name NLog.Mongo.

To install NLog.Mongo, run the following command in the Package Manager Console

PM> Install-Package NLog.Mongo

Configuration Syntax

<extensions>
  <add assembly="NLog.Mongo"/>
</extensions>

<targets>
  <target xsi:type="Mongo"
          name="String"
          connectionName="String"
          connectionString="String"
          collectionName="String"
          cappedCollectionSize="Long"
          cappedCollectionMaxItems="Long"
          includeDefaults="Boolean">

    <!-- repeated --> 
    <field name="String" Layout="Layout" />

    <!-- repeated --> 
    <property name="String" Layout="Layout" />
  </target>
</targets>

Parameters

General Options

name - Name of the target.

Connection Options

connectionName - The name of the connection string to get from the config file.

connectionString - Connection string. When provided, it overrides the values specified in connectionName.

Collection Options

collectionName - The name of the MongoDB collection to write logs to.

cappedCollectionSize - If the collection doesn't exist, it will be create as a capped collection with this max size.

cappedCollectionMaxItems - If the collection doesn't exist, it will be create as a capped collection with this max number of items. cappedCollectionSize must also be set when using this setting.

Document Options

includeDefaults - Specifies if the default document is created when writing to the collection. Defaults to true.

field - Specifies a root level document field. There can be multiple fields specified.

property - Specifies a dictionary property on the Properties field. There can be multiple properties specified.

Examples

Default Configuration with Extra Properties

NLog.config target

<target xsi:type="Mongo"
        name="mongoDefault"
        connectionString="mongodb://localhost/Logging"
        collectionName="DefaultLog"
        cappedCollectionSize="26214400">
  <property name="ThreadID" layout="${threadid}" />
  <property name="ThreadName" layout="${threadname}" />
  <property name="ProcessID" layout="${processid}" />
  <property name="ProcessName" layout="${processname:fullName=true}" />
  <property name="UserName" layout="${windows-identity}" />
</target>

Default Output JSON

{
    "_id" : ObjectId("5184219b545eb455aca34390"),
    "Date" : ISODate("2013-05-03T20:44:11Z"),
    "Level" : "Error",
    "Logger" : "NLog.Mongo.ConsoleTest.Program",
    "Message" : "Error reading file 'blah.txt'.",
    "Exception" : {
        "Message" : "Could not find file 'C:\\Projects\\github\\NLog.Mongo\\Source\\NLog.Mongo.ConsoleTest\\bin\\Debug\\blah.txt'.",
        "Text" : "System.IO.FileNotFoundException: Could not find file 'C:\\Projects\\github\\NLog.Mongo\\Source\\NLog.Mongo.ConsoleTest\\bin\\Debug\\blah.txt' ...",
        "Type" : "System.IO.FileNotFoundException",
        "Source" : "mscorlib",
        "MethodName" : "WinIOError",
        "ModuleName" : "mscorlib",
        "ModuleVersion" : "4.0.0.0"
    },
    "Properties" : {
        "ThreadID" : "10",
        "ProcessID" : "21932",
        "ProcessName" : "C:\\Projects\\github\\NLog.Mongo\\Source\\NLog.Mongo.ConsoleTest\\bin\\Debug\\NLog.Mongo.ConsoleTest.exe",
        "UserName" : "pwelter",
        "Test" : "ErrorWrite",
        "CallerMemberName" : "Main",
        "CallerFilePath" : "c:\\Projects\\github\\NLog.Mongo\\Source\\NLog.Mongo.ConsoleTest\\Program.cs",
        "CallerLineNumber" : "43"
    }
}

Complete Custom Document

NLog.config target

<target xsi:type="Mongo"
        name="mongoCustom"
        includeDefaults="false"
        connectionString="mongodb://localhost/Logging"
        collectionName="CustomLog"
        cappedCollectionSize="26214400">
  <field name="Date" layout="${longdate}" />
  <field name="Level" layout="${level}"/>
  <field name="Message" layout="${message}" />
  <field name="Logger" layout="${logger}"/>
  <field name="Exception" layout="${exception:format=tostring}" />
  <field name="ThreadID" layout="${threadid}" />
  <field name="ThreadName" layout="${threadname}" />
  <field name="ProcessID" layout="${processid}" />
  <field name="ProcessName" layout="${processname:fullName=true}" />
  <field name="UserName" layout="${windows-identity}" />
</target>

Custom Output JSON

{
    "_id" : ObjectId("5187abc2545eb467ecce9184"),
    "Date" : "2013-05-06 08:10:26.5019",
    "Level" : "Debug",
    "Message" : "Sample debug message",
    "Logger" : "NLog.Mongo.ConsoleTest.Program",
    "ThreadID" : "9",
    "ProcessID" : "26604",
    "ProcessName" : "C:\\Projects\\github\\NLog.Mongo\\Source\\NLog.Mongo.ConsoleTest\\bin\\Debug\\v4.5\\NLog.Mongo.ConsoleTest.exe",
    "UserName" : "pwelter"
}

Entity Framework Batch Update and Future Queries

Entity Framework Extended Library

A library the extends the functionality of Entity Framework.

Features

  • Batch Update and Delete
  • Future Queries
  • Audit Log

Project Package and Source

NuGet Package:

Install-Package EntityFramework.Extended

Batch Update and Delete

A current limitations of the Entity Framework is that in order to update or delete an entity you have to first retrieve it into memory. Now in most scenarios this is just fine. There are however some senerios where performance would suffer. Also, for single deletes, the object must be retrieved before it can be deleted requiring two calls to the database. Batch update and delete eliminates the need to retrieve and load an entity before modifying it.

Deleting

//delete all users where FirstName matches
context.Users.Delete(u => u.FirstName == "firstname");

Update

//update all tasks with status of 1 to status of 2
context.Tasks.Update(
    t => t.StatusId == 1, 
    t => new Task {StatusId = 2});

//example of using an IQueryable as the filter for the update
var users = context.Users
   .Where(u => u.FirstName == "firstname");

context.Users.Update(
    users, 
    u => new User {FirstName = "newfirstname"});

Future Queries

Build up a list of queries for the data that you need and the first time any of the results are accessed, all the data will retrieved in one round trip to the database server. Reducing the number of trips to the database is a great. Using this feature is as simple as appending .Future() to the end of your queries. To use the Future Queries, make sure to import the EntityFramework.Extensions namespace.

Future queries are created with the following extension methods...

  • Future()
  • FutureFirstOrDefault()
  • FutureCount()

Sample

// build up queries
var q1 = db.Users
    .Where(t => t.EmailAddress == "one@test.com")
    .Future();

var q2 = db.Tasks
    .Where(t => t.Summary == "Test")
    .Future();

// this triggers the loading of all the future queries
var users = q1.ToList();

In the example above, there are 2 queries built up, as soon as one of the queries is enumerated, it triggers the batch load of both queries.

// base query
var q = db.Tasks.Where(t => t.Priority == 2);
// get total count
var q1 = q.FutureCount();
// get page
var q2 = q.Skip(pageIndex).Take(pageSize).Future();

// triggers execute as a batch
int total = q1.Value;
var tasks = q2.ToList();    

In this example, we have a common senerio where you want to page a list of tasks. In order for the GUI to setup the paging control, you need a total count. With Future, we can batch together the queries to get all the data in one database call.

Future queries work by creating the appropriate IFutureQuery object that keeps the IQuerable. The IFutureQuery object is then stored in IFutureContext.FutureQueries list. Then, when one of the IFutureQuery objects is enumerated, it calls back to IFutureContext.ExecuteFutureQueries() via the LoadAction delegate. ExecuteFutureQueries builds a batch query from all the stored IFutureQuery objects. Finally, all the IFutureQuery objects are updated with the results from the query.

Audit Log

The Audit Log feature will capture the changes to entities anytime they are submitted to the database. The Audit Log captures only the entities that are changed and only the properties on those entities that were changed. The before and after values are recorded. AuditLogger.LastAudit is where this information is held and there is a ToXml() method that makes it easy to turn the AuditLog into xml for easy storage.

The AuditLog can be customized via attributes on the entities or via a Fluent Configuration API.

Fluent Configuration

// config audit when your application is starting up...
var auditConfiguration = AuditConfiguration.Default;

auditConfiguration.IncludeRelationships = true;
auditConfiguration.LoadRelationships = true;
auditConfiguration.DefaultAuditable = true;

// customize the audit for Task entity
auditConfiguration.IsAuditable<Task>()
    .NotAudited(t => t.TaskExtended)
    .FormatWith(t => t.Status, v => FormatStatus(v));

// set the display member when status is a foreign key
auditConfiguration.IsAuditable<Status>()
    .DisplayMember(t => t.Name);

Create an Audit Log

var db = new TrackerContext();
var audit = db.BeginAudit();

// make some updates ...

db.SaveChanges();
var log = audit.LastLog;

Optimize LINQ to SQL with PLINQO Futures

PLINQO future queries are a way to defer query execution until it is needed. The difference between standard IQueryable deferred queries and future queries is that the future queries are batched up and executed in a single round trip to the database.

Read more at PLINQO.com