CKS:API Beta 2 Release Notes

By on September 12, 2011,

CKS:API Beta 2 is live on codeplex: cksapi.codeplex.com.  This post will serve as the release notes for this release.

As with the last release, this project is still in BETA.  It is not ready for production use, but it’s getting closer.  This release is *nearly* fully functional, but there’s a lot of cleanup and performance tweaking that still needs to happen.  The plan is to have that all complete and released by early October.

Call to Action

If you are interested, I would appreciate some feedback.  Please see the Functionality section below for more information, and please consider downloading the bits from Codeplex and testing things out.  If you come across any bugs, please log them to the Issues list on Codeplex – I’ve added some known bugs already.  If you have questions or ideas, please start a Discussion on Codeplex. 

If you are interested in helping out, I’ll be ready for assistance after I get this to a v1 release in a few weeks.  Until that time, it would be harder to bring someone else on board and still hit that deadline.  But please feel free to contact me via Codeplex and give me some ideas of where you would like to help out.

 

Functional Areas

CKS:API currently consists of the following functionality:

  1. Exception Handling and user-friendly display
  2. Logging (Farm & Sandbox)
  3. Code Contracts

Each of these will be detailed below.

CKS:API Design Goals

One of the primary design goals of CKS:API is to be an example of SharePoint development best practices.  It’s not quite there yet, unfortunately.  One of the drawbacks of “side projects” is that there’s a lot of starting and stopping and not a lot of up-front design.  This project has been refactored MANY times and so there are still some ghosts in the machine waiting to be cleared out.

Other design goals include:

  1. Simplicity.  If a utility library doesn’t make things easier/better then why bother using it?  CKS:API is intended to make things easier for developers to write high quality applications.  This does not mean that it is simple internally, but it must be simple to use – at least simpler than writing the code manually.  For example, validating that a series of objects are not null could be written as:
    if (null == exception || null == display || null == throwingControl)
    {
         //throw an error
    }

    .csharpcode, .csharpcode pre
    {
    font-size: small;
    color: black;
    font-family: consolas, “Courier New”, courier, monospace;
    background-color: #ffffff;
    /*white-space: pre;*/
    }
    .csharpcode pre { margin: 0em; }
    .csharpcode .rem { color: #008000; }
    .csharpcode .kwrd { color: #0000ff; }
    .csharpcode .str { color: #006080; }
    .csharpcode .op { color: #0000c0; }
    .csharpcode .preproc { color: #cc6633; }
    .csharpcode .asp { background-color: #ffff00; }
    .csharpcode .html { color: #800000; }
    .csharpcode .attr { color: #ff0000; }
    .csharpcode .alt
    {
    background-color: #f4f4f4;
    width: 100%;
    margin: 0em;
    }
    .csharpcode .lnum { color: #606060; }

    However, using CKS:API allows you to write this instead:

    Contract.RequiresThat(new List<object>() { exception, display, throwingControl }.ValidateAllAreNotNull());

    .csharpcode, .csharpcode pre
    {
    font-size: small;
    color: black;
    font-family: consolas, “Courier New”, courier, monospace;
    background-color: #ffffff;
    /*white-space: pre;*/
    }
    .csharpcode pre { margin: 0em; }
    .csharpcode .rem { color: #008000; }
    .csharpcode .kwrd { color: #0000ff; }
    .csharpcode .str { color: #006080; }
    .csharpcode .op { color: #0000c0; }
    .csharpcode .preproc { color: #cc6633; }
    .csharpcode .asp { background-color: #ffff00; }
    .csharpcode .html { color: #800000; }
    .csharpcode .attr { color: #ff0000; }
    .csharpcode .alt
    {
    background-color: #f4f4f4;
    width: 100%;
    margin: 0em;
    }
    .csharpcode .lnum { color: #606060; }

    Not only is this easier as the number of checks increases, it also gives some context and intention to the code – we’re enforcing a software contract.  The mechanics of throwing the error and maintaining a friendly user experience are hidden from us as there’s no reason for us to have to deal with them every time we want to enforce a contract.

  2. Ease of Use.  If a library places too many requirements on developers before they can even start using it then it is not worth the hassle.  Using CKS:API was  designed to place the smallest number of requirements on the developer as possible.  More details on this are in the Using CKS:API section, but basically it consists of adding the assemblies to your project and your Solution Package and making one method call to prepare the environment.  That’s it.  You’re now ready to start using CKS:API in your projects.
  3. Best Practices.  Yes, I fully realize that this term is so over used as to be almost meaningless, but it is a design goal anyway.  As I mentioned previously, it is not quite there yet, but it is getting there.  What I mean by best practices is simply that the code makes an effort to:
    • be of high quality – there was some thought that went into the code and it wasn’t just tossed together randomly.  This doesn’t mean that every single line is perfect as there are always trade-offs for one thing or another.  It simply means that the code is intentional and the best possible code considering all known situations and trade-offs
    • be consistent – this was a biggie, especially when trying to reconcile sandbox logging and farm logging.  It was important that the experience between the two be as consistent as possible to make things easier for developers.  Sometimes this meant that the implementation details were a bit contrived internally, but the experience for developers is nearly identical whether they are logging to the ULS or to a list (in the case of the sandbox)

Using CKS:API

CKS:API is a utility library to be used in other projects. It is not something to be installed to your SharePoint environment by itself. To that end, it does not consist of typical SharePoint Solutions and Features. Why is this? Simple. You don’t install a utility library just for the sake of it – it is just included as part of whatever “real” application you are installing so why should you have to activate a Feature in SharePoint just to make a utility library available?

Here’s how you would use CKS:API in one of your projects:

  1. Add a reference to the library assemblies in Visual Studio.  There are three assemblies that make up CKS:API:
    1. CKSAPI.dll – (required for all projects).  This is the core library
    2. FarmInstrumentation.dll – required in farm projects
    3. SandboxInstrumentation.dll – required in sandbox projects
  2. Add the assemblies as Additional Assemblies in Package Explorer.
  3. To use logging, instantiate either the CKSFarmInstrumentor or the CKSSandboxInstrumentor object and call it’s InitializeEnvironment method (this should typically be done in your feature receiver)

That’s it.  You can now start using CKS:API in your projects.  For details on what is available and how to use it, see the Functionality section below.

The source code includes 2 simple sample projects that can be used as the basis for testing – one for Sandbox and one for Farm.

Functionality

There are three main areas of functionality for CKS:API: Exception Handling and user-friendly display, Logging (Farm & Sandbox) and Code Contracts.  Details on each of these are provided here.

Code Contracts

First, this is not functionality that is intended to replace or even be compared to what is available in .Net 4.  Conceptually they are similar, but I fully expect to tear this section out of CKS:API once SharePoint can use .Net 4.  In that sense, it is just a stopgap measure to improve the general quality of our code in the short term.

Code Contracts allow you to explicitly state what your software requires in order to operate properly.  For CKS:API, this integrates with the Exception handling functionality to throw a specific error if the contract requirements are not met.  This is implemented as a control class along with a bunch of validations, which are implemented as extension methods on various objects in the .Net framework. 

In general, using a code contract consists of calling the RequiresThat method of the CodeContract class and supplying a condition to be checked.  For example, the following code will ensure that the variable pageName is not null or an empty string:

Contract.RequiresThat(pageName.ValidateHasAValue());

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

    The comparison is functionally equivalent to

    string.IsNullOrEmpty(pageName)

    .csharpcode, .csharpcode pre
    {
    font-size: small;
    color: black;
    font-family: consolas, “Courier New”, courier, monospace;
    background-color: #ffffff;
    /*white-space: pre;*/
    }
    .csharpcode pre { margin: 0em; }
    .csharpcode .rem { color: #008000; }
    .csharpcode .kwrd { color: #0000ff; }
    .csharpcode .str { color: #006080; }
    .csharpcode .op { color: #0000c0; }
    .csharpcode .preproc { color: #cc6633; }
    .csharpcode .asp { background-color: #ffff00; }
    .csharpcode .html { color: #800000; }
    .csharpcode .attr { color: #ff0000; }
    .csharpcode .alt
    {
    background-color: #f4f4f4;
    width: 100%;
    margin: 0em;
    }
    .csharpcode .lnum { color: #606060; }

    but by using the code contract, we also get the exception handling built in as well as opportunities for additional logging if we so desire.  Preliminary performance testing indicates that the first usage of a code contract is marginally slower than the equivalent code (due to the loading of the additional assembly?) but subsequent usage in a request incurs no performance hit.

    If the contract is not satisfied, we will get a log entry similar to the following (in a Farm Solution):

    image

    (You would get a similar log entry in a Sandbox solution.)  Note that there is currently an open issue getting the name of the parameter that failed validation, which is why it shows up as “Unknown.”  Because we’re using the exception framework in CKS:API, the user sees a nice user-friendly message  – more on that later.

    Currently, the following validations are supported as extensions to specific objects:

    String Double & Float Int
    ValidateHasAValue ValidateIsGreaterThanOrEqualTo ValidateIsOdd
    ValidateIsShorterThanOrEqualTo ValidateIsLessThanOrEqualTo ValidateIsEven
    ValidateIsLongerThanOrEqualTo ValidateIsGreaterThan ValidateIsGreaterThanOrEqualTo
    ValidateIsLongerThan ValidateIsLessThan ValidateIsLessThanOrEqualTo
    ValidateDoesNotContain ValidateIsEqualTo ValidateIsGreaterThan
    ValidateContains ValidateIsNotEqualTo ValidateIsLessThan
    ValidateDoesNotEndWith   ValidateIsEqualTo
    ValidateEndsWith Object ValidateIsNotEqualTo
    ValidateDoesNotBeginWith ValidateIsNotType<T>  
    ValidateBeginsWith ValidateIsType<T> IEnumerable
    ValidateIsExactLength ValidateIsNull ValidateAllAreNotNull
    ValidateIsShorterThan ValidateIsEqualTo ValidateAnyAreNull
    ValidateIsNotExactLength ValidateIsNotEqualTo  
         
    Guid    
    ValidateIsNotEmptyGuid    
    ValidateIsEmptyGuid    

    Question: Each validation currently begins with the prefix “Validation” which makes them easily discoverable, but personally I don’t like what it does to the code readability – it makes the code stilted.  What do you think – keep the prefix or drop it?

    There is also a related piece of functionality in the Code Contract part of CKS:API – the ability to simply perform a check of a validation without throwing the error:

    Check.ThatAllAreNotNull(SPContext.Current, myVar, myVar2)

    .csharpcode, .csharpcode pre
    {
    font-size: small;
    color: black;
    font-family: consolas, “Courier New”, courier, monospace;
    background-color: #ffffff;
    /*white-space: pre;*/
    }
    .csharpcode pre { margin: 0em; }
    .csharpcode .rem { color: #008000; }
    .csharpcode .kwrd { color: #0000ff; }
    .csharpcode .str { color: #006080; }
    .csharpcode .op { color: #0000c0; }
    .csharpcode .preproc { color: #cc6633; }
    .csharpcode .asp { background-color: #ffff00; }
    .csharpcode .html { color: #800000; }
    .csharpcode .attr { color: #ff0000; }
    .csharpcode .alt
    {
    background-color: #f4f4f4;
    width: 100%;
    margin: 0em;
    }
    .csharpcode .lnum { color: #606060; }

    This makes use of the same contract framework, but instead of throwing an error, it simply returns a boolean.  All of the same validations are supported.

    Exceptions

    CKS:API provides a couple of standard custom errors currently and there are plans to define more.  Right now it supports:

    • CKSException – the base class for all CKS:API exceptions
    • CKSContractException : when a contract is violated
    • CKSInvalidOperationException: when an invalid operation happens
    • CKSValidationException: when a condition check fails validation
    • CKSWrapperException: used to wrap a standard .Net exception to provide a consisten logging and end user experience even when the standard .Net exception classes are used.  This also allows us to support the processing of unhandled exceptions.

    The goal behind the exception handling is to provide detailed information to the administrators and developers while showing a consistent, friendly error message to the end users.

    Depending on how the developer uses the framework, the user will see either a StatusBar message:

    or a dialog box:

    image

    [NOTE: The contents of this user error page are currently hardcoded into CKS:API and are not functional.  I’m not sure that CKS:API will ever implement this and it will likely be left up to anyone using the product to define the error page they want their users to see.]

    In some cases (notably when an unhandled exception occurs, the user sees the same dialog page but in the full browser window instead of the modal dialog box.  If the user click the “Help” link in the StatusBar, they will be shown the dialog box.

    From the developers perspective, things couldn’t be easier:

     try
    {
        throw new CKSInvalidOperationException("oops...something bad happened");
    }
    catch (Exception ex)
    {
        instrumentor.HandleException(ex, ErrorDisplayType.StatusBar, this);
    }

    .csharpcode, .csharpcode pre
    {
    font-size: small;
    color: black;
    font-family: consolas, “Courier New”, courier, monospace;
    background-color: #ffffff;
    /*white-space: pre;*/
    }
    .csharpcode pre { margin: 0em; }
    .csharpcode .rem { color: #008000; }
    .csharpcode .kwrd { color: #0000ff; }
    .csharpcode .str { color: #006080; }
    .csharpcode .op { color: #0000c0; }
    .csharpcode .preproc { color: #cc6633; }
    .csharpcode .asp { background-color: #ffff00; }
    .csharpcode .html { color: #800000; }
    .csharpcode .attr { color: #ff0000; }
    .csharpcode .alt
    {
    background-color: #f4f4f4;
    width: 100%;
    margin: 0em;
    }
    .csharpcode .lnum { color: #606060; }(instrumentor, in this example, is an instance of a CKSFarmInstrumentor object). 

    Detailed logging information is either written to the ULS log for Farm solutions (as seen above for the Contract) or to a SharePoint list, for Sandbox solutions:

    image

    Notice that the sandbox logger makes use of append-only fields and batches the information written into groups to reduce the number of writes to the SharePoint DB, and therefore the number of resources used.  See below for more details on each logger.

    Handling standard .Net exceptions is identical to handling CKS exceptions – simply call HandleException on your instrumentor and pass in the exception, how you want the information displayed (choices are StatusBar, DialogBox or None) and a reference to the object that threw the error (this).  CKS:API takes over from there and handles it all for you.

    The last piece of functionality in the Exception framework is the handling of otherwise unhandled errors.  If an error happens anywhere in the processing of your page that you do not catch and deal with, instead of seeing a standard error message, the users will be shown the dialog box page (not inside the modal dialog box though).  Note that there are currently a few bugs in this section:

    1. The exception information is not currently logged for unhandled exceptions
    2. Sandbox solutions are not shown any error message.  They simply see their page with no indication that anything happened.  This is better than the standard “an error has occurred in the target of an invocation”  message that users love so much, but not ideal.  Hopefully this will get resolved in the next release.

    Logging

    The last piece of functionality included in CKS:API is a logging framework.  The goal of this piece is to provide a consistent, highly functional logging solution regardless of whether the code is running in the sandbox or in a farm.  To that end, CKS:API provide the following methods:

    • LogError
    • LogInformation
    • LogWarning
    • WriteToDeveloperDashboard
    • LogDebug
    • LogMethodStart
    • LogMethodEnd

    The first three are fairly obvious in what they do.  The last four might need a little explanation:

    • WriteToDeveloperDashboard: writes a message to the Developer Dashboard (duh) in a somewhat more simplified way than what you get out of the box.  In addition to supporting the standard SPMonitoredScope approach (currently only one implementation is included), it also provides the ability to write arbitrary messages to the Dashboard.  This all works for a Farm environment just fine.  What I’m hoping to do is extend the same (or at least similar) functionality to Sandbox solutions as well, something not generally considered possible.  I’ve got a few ideas that I hope to get in place in the next release.
    • LogDebug: Gives the developer the option of writing messages to the log that will only execute when the assembly is compiled as a DEBUG build.  When they compile their project in a RELEASE build, the calls to this method are automatically stripped out.  This means that the developer can go crazy writing debug information into the log while they are developing their code and not have to worry about stripping them out before doing a Release build.  The compiler does it for them by means of the conditional compilation directives available in Visual Studio.  There is exactly zero performance impact to that extra logging because the calls are simply not included in the Release build.
    • LogMethodStart/LogMethodEnd: This is part of that “going crazy” writing debug information into the logs I mentioned earlier.  Adding this call to the beginning/end of your methods will automatically create a log entry with the name of the method and the time.  This can be useful for tracing through your code to uncover nasty little Heisenbugs.  These both make use of the same conditional compilation as LogDebug so there’s no reason to remove these calls from your source code before compiling a RELEASE build. 

    Closing

    That about wraps up the information for beta 2 of CKS:API.  Please take it for a spin and let me know what you think.  Just remember the standard developer disclaimer – this is still a work in progress so no laughing at my code   Smile

    Feedback

    Remember that this is a beta, but please post any issues to the Issue Tracker on Codeplex, or any questions to the Discussion forum

    Future Releases

    I am currently planning the following release schedule:

    • Production-ready 1.0 – October 2011.
    • Bug Fixes 1.1 – 1.9 as needed.

    -Dave

    Tags , , ,