Wednesday, September 5, 2018

Using Caching in the .NET Framework

Caching is easy to use in the .NET Framework

According to Microsoft:
"In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications."

Here are a couple methods to Load and Save from the Memory Cache.  If the value is not found, it will be null.  

public static class Cache
{
        public static TResult Load<TResult>(string cacheKeyName)
        {
            ObjectCache cacheInstance = MemoryCache.Default;
            return (TResult)cacheInstance[cacheKeyName];
        }

        public static T Save<T> (string cacheKeyName, DateTimeOffset expirationDate, T value)
        {
            CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
            cacheItemPolicy.AbsoluteExpiration = expirationDate;
            ObjectCache cacheInstance = MemoryCache.Default;
            cacheInstance.Set(cacheKeyName, value, cacheItemPolicy);
            return value;
        }
}

Example Call to Load and Save


Here is an example of using the load and save with a stopwatch so that the time saved can be shown:


        [Test]
        public void SimpleLoadAndSaveTest()
        {
            Stopwatch watch = Stopwatch.StartNew();
            string json = Cache.Load<string>("JsonValue");

            if (json == null)
            {
                json = GetJsonFromWebService("cf4174ce-96d2-495c-b58a-427d35f64a20");
                Cache.Save("JsonValue", DateTimeOffset.Now.AddMinutes(20), json);
            }
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Restart();
            json = Cache.Load<string>("JsonValue");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
        }

        public string GetJsonFromWebService(string value)
        {
            Thread.Sleep(1000);
            return "{ \"Value\" : \"" + value + "\" }";
        }

Cache Class


I have created a larger caching class where methods can be passed into shorten the code:


    public static class Cache
    {
        public static TResult Load<TResult>(string cacheKeyName)
        {
            ObjectCache cacheInstance = MemoryCache.Default;
            return (TResult)cacheInstance[cacheKeyName];
        }

        public static T Save<T> (string cacheKeyName, DateTimeOffset expirationDate, T value)
        {
            CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
            cacheItemPolicy.AbsoluteExpiration = expirationDate;
            ObjectCache cacheInstance = MemoryCache.Default;
            cacheInstance.Set(cacheKeyName, value, cacheItemPolicy);
            return value;
        }


        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<TResult> serviceFunction)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction);
        }

        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<string, TResult> serviceFunction, string stringArg)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction, stringArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<long, TResult> serviceFunction, long longArg)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction, longArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, int expirationMinutes, Func<int, TResult> serviceFunction, int intArg)
        {
            return Load<TResult>(cacheKeyName, DateTimeOffset.Now.AddMinutes(expirationMinutes), serviceFunction, intArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<TResult> serviceFunction)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<string, TResult> serviceFunction, string stringArg)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction, stringArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<long, TResult> serviceFunction, long longArg)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction, longArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTime expirationDate, Func<int, TResult> serviceFunction, int intArg)
        {
            return Load<TResult>(cacheKeyName, new DateTimeOffset(expirationDate), serviceFunction, intArg);
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<TResult> serviceFunction) 
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {                
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction());
            }

            return cachedObject;
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<string, TResult> serviceFunction, string stringArg)
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction(stringArg));
            }

            return cachedObject;
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<long, TResult> serviceFunction, long longArg)
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction(longArg));
            }

            return cachedObject;
        }

        public static TResult Load<TResult>(string cacheKeyName, DateTimeOffset expirationDateOffset, Func<int, TResult> serviceFunction, int intArg)
        {
            TResult cachedObject = Load<TResult>(cacheKeyName);

            if (cachedObject == null)
            {
                cachedObject = Save(cacheKeyName, expirationDateOffset, serviceFunction(intArg));
            }

            return cachedObject;
        }

    }

Example using Func with Caching

The function can be passed in that will fill the data if it does not exist in the cache.

        [Test]
        public void CacheTestUsingFunc()
        {
            Stopwatch watch = Stopwatch.StartNew();
            string json = Cache.Load<string>("JsonValue", 20, GetJsonFromWebService, "cf4174ce-96d2-495c-b58a-427d35f64a20");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);

            watch.Restart();
            json = Cache.Load<string>("JsonValue", 20, GetJsonFromWebService, "cf4174ce-96d2-495c-b58a-427d35f64a20");
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
        }

        public string GetJsonFromWebService(string value)
        {
            Thread.Sleep(1000);
            return "{ \"Value\" : \"" + value + "\" }";
        }

Thursday, June 1, 2017

Debugging Jasmine Tests with Karma and Visual Studio Code

  1. The Visual Studio Code Debugger only works with specific combinations of the Debugger for Chrome, Visual Studio Code, Angular CLI and Angular.  Make sure you are using the latest version of all libraries, extensions and tools or you will get the dreaded Breakpoint ignored because generated code not found.  For example, Angular 4.0 with the 1.0 version of Angular CLI does not work but Angular 4.1.3 with Angular CLI 1.0.6 works fine.
  2. Install the Visual Studio Code Extension, Chrome Debugger by clicking on the extensions icon on the left in Visual Code, searching for Chrome and clicking Install.
  3. On the Debug menu, click Open Configurations.




4. Modify your .vscode\launch.json to be this:
{
"version""0.2.0",
"configurations": [
{
"type""chrome",
"request""launch",
"name""Launch Chrome against localhost",
"webRoot""${workspaceRoot}"
},
{
"type":"chrome",
"request""launch",
"name""Launch Chrome against Karma",
"webRoot""${workspaceRoot}"
}
]
}
5.  CLOSE ALL Chrome Browsers
6.  Run npm start at the command line in the directory for your application.

7. Run karma start at another command window in the directory for your application
8. Click the Debug icon on the left
9. Set a breakpoint
10. Choose Launch Chrome against Karma.  After the browser comes up, refresh it.




Friday, April 28, 2017

Jasmine Examples

Example of a Simple Test

describe('a super simple test', () => {
it('should be true', () => {
expect(true).toBe(true);
});
it('should be false', () => {
expect(false).toBe(false);
});
});

Setup and Teardown


describe("A set of tests using beforeEach and afterEach", () => {
var x = 0;
beforeEach(function() {
x += 1;
});
afterEach(function() {
x = 0;
});
it("is just a function, so it can contain any code", () => {
expect(x).toEqual(1);
});
it("can have more than one expectation", () => {
expect(x).toEqual(1);
expect(true).toEqual(true);
});
});

Expect Matching


expect(myValue).toBe(true); // Use JS strict equality
expect(myValue).not.toBe(true);
expect(myValue).toEqual(482); // Use deep equality, recursive search through objects
expect(myValue).toBeDefined();
expect(myValue).not.toBeDefined();
expect(myValue).toBeUndefined();
expect(myValue).toBeTruthy(); // Boolean cast testing
expect(myValue).toBeFalsy();
expect(myValue).toContain('sometext'); // Find item in array
expect(e).toBeLessThan(pi);
expect(pi).toBeGreaterThan(e);
expect(x).toBeCloseTo(y, 2); // x to be close to y by 2 decimal points
Exception Matching
expect(function() {
MyMethod(1, '2')
}).toThrowError();
expect(function() {
MyMethod(1, '2')
}).toThrow(new Error('Invalid parameter type.')

Spies

Spies can stub any function and tracks calls to it and all arguments.
A spy only exists in the describe or it block in which it is defined, and will be removed after each spec.

describe('SuperAwesomeModule', function() {
beforeEach(function() {
// track all calls to SuperAwesomeModule.coolHelperFunction()
// and also delegate to the actual implementation
spyOn(SuperAwesomeModule, 'coolHelperFunction').and.callThrough();
});
describe('featureA', function() {
it('should ...', function() {
expect(SuperAwesomeModule.featureA(2)).toBe(5);

// matchers for spies
expect(SuperAwesomeModule.coolHelperFunction).toHaveBeenCalled();
expect(SuperAwesomeModule.coolHelperFunction).toHaveBeenCalledTimes(1);
});
});
});
Spies: and.returnValue
Useful when you want to stub out return values
describe('SuperAwesomeModule', function() {
beforeEach(function() {
spyOn(SuperAwesomeModule, 'coolHelperFunction').and.returnValue('myValue');
});
});

Karma Resources

Karma is a test runner for testing JavaScript and TypeScript.  
Install Karma
yarn global add karma-cli
Run all tests
karma start 
Testing Angular 2 Apps with Jasmine and Karma
https://www.youtube.com/watch?v=yG4FH60fhUE
Unit Testing Recipes for Angular 2
http://slides.com/victormejia/unit-testing-ng2#/ 

Example karma.conf.js


This file uses the new Angular CLI (the namespace has changed).  

// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
files: [
{ pattern: './src/test.ts', watched: false }
],
preprocessors: {
'./src/test.ts': ['@angular/cli']
},
mime: {
'text/x-typescript': ['ts','tsx']
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: config.angularCli && config.angularCli.codeCoverage
? ['progress', 'coverage-istanbul']
: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};