Showing posts with label ASP.NET. Show all posts
Showing posts with label ASP.NET. Show all posts

Thursday, April 27, 2017

Creating an IOC Container for ASP.NET Core

I recently added support for ASP.NET Core to my Tact.NET IOC Container, and I thought that I would share some of the interesting requirements I discovered while doing so. I had originally expected the default ASP.NET Core container would follow the same rules as the official Microsoft Unity container, but that turned out not to be the case!

1) Register is first in win.

The Unity container was last in win, and it would completely disregard the original registration. With ASP.NET Core you need to preserve the original registration and then treat all subsequent registrations as addition registrations that will only be resolved when ResolveAll is invoked, similar to keyed registrations in Unity. Which brings us to our next difference...

public class RegisterIsFirstInWinTests
{
 [Fact]
 public void Unity()
 {
 var log = new EmptyLog();
 using (var container = new TactContainer(log))
 {
 container.RegisterSingleton<IExample, ExampleA>();
 container.RegisterSingleton<IExample, ExampleB>();
 
 var example = container.Resolve<IExample>();
 Assert.IsType<ExampleB>(example);
 }
 }
 
 [Fact]
 public void AspNetCore()
 {
 var log = new EmptyLog();
 using (var container = new AspNetCoreContainer(log))
 {
 container.RegisterSingleton<IExample, ExampleA>();
 container.RegisterSingleton<IExample, ExampleB>();
 
 var example = container.Resolve<IExample>();
 Assert.IsType<ExampleA>(example);
 }
 }
 
 public interface IExample
 {
 string Name { get; }
 }
 
 public class ExampleA : IExample
 {
 public string Name => nameof(ExampleA);
 }
 
 public class ExampleB : IExample
 {
 public string Name => nameof(ExampleB);
 }
}

Sunday, January 29, 2017

.NET JsonContent for HttpClient

.NET already comes with a nice collection of HttpContent serializers, but it lacks a JsonContent type. A common solution is to just serialize their payload to a JSON string and that insert that into an instance of StringContent. However, this means that you need to remember to set your headers, and it is a little bit inefficient because of how it creates multiple strings and buffers for each payload.

I have create a simple implementation of JsonContent that uses Json.NET and pooled memory streams. The result is between 2% and 10% faster, and causes ~50% fewer garbage collections.

Check out the implementation in Tact.NET:

Enjoy,
Tom

Friday, September 30, 2016

Host HTTP and WebSockets on the Same Port in ASP.NET

How do you support WebSocket connections on the same port as your HTTP server in .NET? It turns out that this is not that hard, and you even have several choices to do so...

Option 1: TCP Proxy

You could use a TCP proxy port to divide traffic between the two targets. For example, all traffic would come in to port 80, but then HTTP traffic would be routed internally to 8001 and WS traffic to 8002. This is useful because it would allow you to use multiple technologies (in the example on their site, both NancyFX and Fleck) to host your web servers.

Option 2: SignalR

SignalR is great because of all the backwards compatibility that it supports for both the server and client. However, it is not the most lightweight framework. If you choose to use it, then it will absolutely support both HTTP and WS.

Option 3: Owin.WebSocket *My Favorite*

Owin supports WebSockets, and that is exactly what SignalR uses to host its WS connections. The awesome Mr. Bryce Godfrey extracted the Owin code from SignalR into a much smaller library called Owin.WebSocket.

The ONLY thing that I did not like about this implementation was that is uses inheritance to define endpoints, whereas I much prefer the ability to use delegates and lambdas. Because of this, I created Owin.WebSocket.Fleck, which allows you to use the Fleck API to map your WebSockets to an Owin context. A pull request is open to merge this into the main repository.

Enjoy,
Tom

Sunday, February 21, 2016

Async Cache Repository v2

Three years ago (wow, time flies) I wrote a generic Cache Repository that has become one of my more popular open source projects. It is 2016, so was definitely time to create an async version of that cache repository! This new implementation has all of the same features as the original, only now it is completely async from top to bottom.

CacheRepository.Web

Features

  • Thread Safe GetOrSet
  • Configurable Expiration Enums
  • Transparent Cache Key Management By Type
  • A Web Implementation

NuGet Package and Source

Enjoy,
Tom

Sunday, June 8, 2014

How to stream a FileResult from one web server to another with ASP.NET MVC

MVC has a lot of great built in tooling, including the ability to stream very large file results straight from disk without having to load the whole file stream into memory.

What about the scenario where you want to stream a large file from one web server to another?

For example, I have an ASP.NET MVC application that needs to expose a download for a file hosted on another server, but I can not just redirect my users directly to the other URL. For that, we need to create a custom ActionResult type!

WebRequestFileResult

Here is a simple of example of what your controller might look like:

public class FileController : Controller
{
 public ActionResult LocalFile()
 {
 return new FilePathResult(@"c:\files\otherfile.zip", "application/zip");
 }
 
 public ActionResult RemoteFile()
 {
 return new WebRequestFileResult("http://otherserver/otherfile.zip");
 }
}

Saturday, October 26, 2013

Bootstrap 3, LESS, Bundling, and ASP.NET MVC

Until Twitter Bootstrap v3, I would have recommend that you use dotLess to compile and bundle your LESS files. However, it is now a known issue that the current build of dotLess does not support Bootstrap 3, or more specifically that it does not support LESS 1.4; and worse yet, there is no fix in sight.

So, how can you use Bootstrap LESS with MVC?

I recommend using BundleTransformer. It is an amazingly feature rich set of extensions for System.Web.Optimization. The BundleTransformer.Less extension provides easy to use LESS transformations (already up to LESS 1.5) and bundling support that wires up straight into your pre-existing BundleCollection configuration. For more information about everything that BundleTransformer has to offer, check out this article.

Now here is how you setup Bootstrap 3 and BundleTransformer for ASP.NET:

Required NuGet Packages

  1. Twitter.Bootstrap.Less
  2. BundleTransformer.Less
  3. BundleTransformer.MicrosoftAjax
  4. JavaScriptEngineSwitcher.Msie

Tuesday, August 13, 2013

Control Minification per Request with Web Optimizations

The Microsoft ASP.NET Web Optimization Framework is a great bundling and minification solution for your web applications. Simply grab the Microsoft.AspNet.Web.Optimization NuGet package, register your bundles, render them with a single line of code, and your environment will automatically resolve your dependencies based on whether or not the web server is running in debug mode.

But how can you debug minified styles and scripts in production?

Normally that is a difficult proposition, but here is a simple solution: JUST DON'T MINIFY THEM! With the little code snippets below you can add a simple query string parameter to disable minification for specific sessions or requests.

Adding this functionality to your website is extremely easy and requires no additional dependencies. Web Optimizations already has an internal AssetManager class that supports this functionality, we just need to access it via reflection.

Simply apply the following two steps and you will be ready to debug in production:

  1. Create the HtmlHelperExtensions class with the code below.
  2. Add a call to TrySetOptimizationEnabled inside of your ViewStart.

_ViewStart.cshtml

@using System.Web.Optimization
@{
 Layout = "~/Views/Shared/_Layout.cshtml";
 Html.TrySetOptimizationEnabled();
}

HtmlHelperExtensions.cs

public static class HtmlHelperExtensions
{
 public const string Key = "OptimizationEnabled";
 public static bool TrySetOptimizationEnabled(this HtmlHelper html)
 {
 var queryString = html.ViewContext.HttpContext.Request.QueryString;
 var session = html.ViewContext.HttpContext.Session;
 // Check the query string first, then the session.
 return TryQueryString(queryString, session) || TrySession(session);
 }
 private static bool TryQueryString(
 NameValueCollection queryString, 
 HttpSessionStateBase session)
 {
 // Does the query string contain the key?
 if (queryString.AllKeys.Contains(
 Key, 
 StringComparer.InvariantCultureIgnoreCase))
 {
 // Is the value a boolean?
 bool boolValue;
 var stringValue = queryString[Key];
 if (bool.TryParse(stringValue, out boolValue))
 {
 // Set the OptimizationEnabled flag
 // and then store that value in session.
 SetOptimizationEnabled(boolValue);
 session[Key] = boolValue;
 return true;
 }
 }
 return false;
 }
 private static bool TrySession(HttpSessionStateBase session)
 {
 if (session != null)
 {
 var value = session[Key] as bool?;
 if (value.HasValue)
 {
 // Use the session value to set the OptimizationEnabled flag.
 SetOptimizationEnabled(value.Value);
 return true;
 }
 }
 return false;
 }
 private static void SetOptimizationEnabled(bool value)
 {
 // Use reflection to set the internal AssetManager.OptimizationEnabled
 // flag for this request specific.
 var instance = ManagerProperty.GetValue(null, null);
 OptimizationEnabledProperty.SetValue(instance, value);
 }
 private static readonly PropertyInfo ManagerProperty = typeof(Scripts)
 .GetProperty("Manager", BindingFlags.Static | BindingFlags.NonPublic);
 private static readonly PropertyInfo OptimizationEnabledProperty = Assembly
 .GetAssembly(typeof(Scripts))
 .GetType("System.Web.Optimization.AssetManager")
 .GetProperty(
 "OptimizationEnabled",
 BindingFlags.Instance | BindingFlags.NonPublic);
}
Shout it

Enjoy,
Tom

Thursday, August 1, 2013

PhantomJS, the Headless Browser for your .NET WebDriver Tests

Did you know that Selenium already supports PhantomJS?

WebDriver is a specification for controlling the behavior of a web browser. PhantomJS is a headless WebKit scriptable with a JavaScript API. Ghost Driver is a WebDriver implementation that uses PhantomJS for its back-end. Selenium is a software testing framework for web applications. Selenium WebDriver is the successor to Selenium RC. The Selenium WebDriver NuGet Package is a .NET client for for Selenium WebDriver that includes support for PhantomJs via GhostDriver.

NuGet Packages

You need only install two NuGet packages in order to use PhantomJS with WebDriver. You will probably also want which ever Unit Testing framework you prefer. As always, I suggest xUnit.

  1. Selenium.WebDriver
  2. phantomjs.exe

PhantomJSDriver

After installing those, using the PhantomJSDriver is as easy as any other WebDriver!

const string PhantomDirectory =
 @"..\..\..\packages\phantomjs.exe.1.8.1\tools\phantomjs";
[Fact]
public void GoogleTitle()
{
 using (IWebDriver phantomDriver = new PhantomJSDriver(PhantomDirectory))
 {
 phantomDriver.Url = "http://www.google.com/";
 Assert.Contains("Google", phantomDriver.Title);
 }
}
Shout it

Enjoy,
Tom

Friday, July 5, 2013

Microsoft Build 2013 for Web Developers

Build 2013 was a whole lot of fun!

The big reveals at Build 2013 was Windows 8.1 and Visual Studio 2013. Admittedly Microsoft's big focus was on the Windows 8 store, but that does not mean that they are not delivering great tools for the fastest growing development environment in the world: the web!

Before we discuss web development, let's first take a moment to talk about Windows.

Windows 8.1 actually looks really good! Like all Microsoft products, the first major patch manages to iron out most of the kinks. The new UI updates and inclusion of the start button make the modern UI and desktop seem much less polarizing. Additionally, I am very optimistic about seeing Windows 8 applications come to PC, table, and phone. Microsoft has always had great potential for creating a homogeneous ecosystem consumer devices, and I am finally getting excited about the product line up that they are delivering.

New Editors in Visual Studio 2013

The Visual Studio Team has completely rewritten the HTML and JavaScript editors from scratch in Visual Studio 2013. This is great news; not only are the new editors faster, more responsive, and have much better intellisense, but they also come with a set of fun new features.

  • The overall Razor experience has been greatly improved.
  • Intellisense now include expandable groups to keep drop downs from getting cluttered.
  • CSS intellisense now displays browser compatibility for each rule.
  • New shift alt keys allows you to select content from tree structures.

Microsoft has yet to announce how they will charge for Visual Studio 2013. Will it be a stand alone install, will it be full price, we just do not know yet. Personally, I would love to see Visual Studio more to incremental update system, but obviously there would be lot of technical limitation to doing so...but hey, a dev can dream!

HTML Editing Features in Visual Studio 2013 Preview

Browser Link

Visual Studio 2013 comes with a built in feature called Browser Link. This is where Visual Studio opens a socket connection directly to your browsers (yes, plural) via SignalR that allows your IDE to send commands directly to the browser itself. The most basic use of this a simple refresh button that allows you to refresh all your browser windows on command.

The potential of this feature is fun to think about! Right now the Visual Studio team is intentionally keeping their default feature set for Browser link very simple, but also open source; as they want to see what us developers come up with. Remember that this is a two way channel of communication between the browser and the IDE, so sending error messages back to Visual Studio is just the first thing that comes to my mind.

A cool demo that they showed us at Build was keeping multiple browser windows in sync, this included form elements and page navigation. The cool part was that these browsers included Chrome, Firefox, and IE running on a virtualized windows phone. It was a very compelling demo!

Browser Link feature in Visual Studio Preview 2013

Web API 2.0

Web API was a great addition to Microsoft's web development library, and 2.0 looks like a great incrimental update to that framework. Forgive me if it seems that I do not have much to say about Web API 2, it just seems like a very simple but very welcome update to the framework; but don't let that fool you, I am stoked!

  • Attribute based routing.
  • Integrated OAuth 2 authentication.
  • OWIN Web hosting (run your APIs outside of IIS)
  • Cross-Origin Resource Sharing

ASP.NET Web API 2

TypeScript 0.9

I am SO excited about TypeScript!

TypeScript is a development language that is a super set of JavaScript. It adds type checking, interfaces, and inheritance to JavaScript. The idea is that you develop in TypeScript and then compile that language down to JavaScript for deployment on the web. The key here is that TypeScript is a super set of JavaScript, so all JavaScript code is already valid TypeScript, making it very easy for you to start your migration from language to the other.

There are essentially two ways that you could choose to use TypeScript:

  1. Use TypeScript for simple type checking and enhanced refactoring.

This is probably the more ideal use of TypeScript, or at least the simplest. The idea being that you can constrain your function parameters by type and interface, and make use of TypeScripts more strick declarations for easier refactoring. You would also get significantly improved intellisense both for your code and other frameworks, as a very active open source community has been creating interface wrappers for everything from JQuery to Knockout.

  1. Go full monty, and make your JavaScript completely Object Oriented.

Using TypeScript would allow you to define and extend both classes and interfaces, just as you would a language like C#. This opens the possibility for polymorphism, dependency injection, mocking, testing, the whole object oriented play book. While this does go against many principals of a very functional programming language like JavaScript, it also enables a lot of best practices that help simplify large scale application development.

...how far you want to go using TypeScript is up to you, but regardless of which side of the fence you are on, I definitely feel that TypeScript is worth looking into!

www.typescriptlang.org

Shout it

The future looks bright,
Tom

Saturday, April 13, 2013

Report Unhandled Errors from JavaScript

Logging and aggregating error reports is one of the most important things you can do when building software: 80% of customer issues can be solved by fixing 20% of the top-reported bugs.

Almost all websites at least has some form of error logging on their servers, but what about the client side of those websites? People have a tendency to brush over best practices for client side web development because "it's just some scripts." That, is, WRONG! Your JavaScript is your client application, it is how users experience your website, and as such it needs the proper attention and maintenance as any other rich desktop application.

So then, how do you actually know when your users are experiencing errors in their browser? If you are like the vast majority of websites out there...

You don't know about JavaScript errors, and it's time to fix that!

window.onerror

Browsers do offer a way to get notified of all unhandled exceptions, that is the window.onerror event handler . You can wire a listener up to this global event handler and get back three parameters: the error message, the URL of the file in which the script broke, and the line number where the exception was thrown.

window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
 // TODO: Something with this exception!
 // Just let default handler run.
 return false;
}

StackTrace.js

JavaScript can throw exceptions like any other language; browser debugging tools often show you a full stack trace for unhandled exceptions, but gathering that information programmatically is a bit more tricky. To learn a bit more about the JavaScript language and how to gather this information yourself, I suggest taking a look at this article by Helen Emerson. However in practice I would strongly suggest you use a more robust tool...

StackTrace.js is a very powerful library that will build a fully detailed stack trace from an exception object. It has a simple API, cross browser support, it handles fringe cases, and is very light weight and unobtrusive to your other JS libraries.

try {
 // error producing code
} catch(error) {
 // Returns stacktrace from error!
 var stackTrace = printStackTrace({e: error});
}

Two Big Problems

  1. The window.onerror callback does not contain the actual error object.

This is a big problem because without the error object you cannot rebuild the stack trace. The error message is always useful, but file names and line numbers will be completely useless once you have minified your code in production. Currently, the only way you can bring additional information up to the onerror callback is to try catch any exceptions that you can and store the error object in a closure or global variable.

  1. If you globally try catch event handlers it will be harder to use a debugger.

It would not be ideal to wrap every single piece of code that you write in an individual try catch block, and if you try to wrap your generic event handling methods in try catches then those catch blocks will interrupt your debugger when you are working with code in development.

Currently my suggestion is to go with the latter option, but only deploy those interceptors with your minified or production code.

jQuery Solution

This global error handling implementation for jQuery and ASP.NET MVC is only 91 lines of JavaScript and 62 lines of C#.

Download JavaScriptErrorReporter from GitHub

To get as much information as possible, you need to wire up to three things:
(Again, I suggest that you only include this when your code is minified!)

  1. window.onerror
  2. $.fn.ready
  3. $.event.dispatch

Here is the meat of those wireups:

var lastStackTrace,
 reportUrl = null,
 prevOnError = window.onerror,
 prevReady = $.fn.ready,
 prevDispatch = $.event.dispatch;
// Send global methods with our wrappers.
window.onerror = onError;
$.fn.ready = readyHook;
$.event.dispatch = dispatchHook;
function onError(error, url, line) {
 var result = false;
 try {
 // If there was a previous onError handler, fire it.
 if (typeof prevOnError == 'function') {
 result = prevOnError(error, url, line);
 }
 // If the report URL is not loaded, load it.
 if (reportUrl === null) {
 reportUrl = $(document.body).attr('data-report-url') || false;
 }
 // If there is a rport URL, send the stack trace there.
 if (reportUrl !== false) {
 var stackTrace = getStackTrace(error, url, line, lastStackTrace);
 report(error, stackTrace);
 }
 } catch (e) {
 // Something went wrong, log it.
 if (console && console.log) {
 console.log(e);
 }
 } finally {
 // Clear the wrapped stack so it does get reused.
 lastStackTrace = null;
 }
 return result;
}
function readyHook(fn) {
 // Call the original ready method, but with our wrapped interceptor.
 return prevReady.call(this, fnHook);
 function fnHook() {
 try {
 fn.apply(this, arguments);
 } catch (e) {
 lastStackTrace = printStackTrace({ e: e });
 throw e;
 }
 }
}
function dispatchHook() {
 // Call the original dispatch method.
 try {
 prevDispatch.apply(this, arguments);
 } catch (e) {
 lastStackTrace = printStackTrace({ e: e });
 throw e;
 }
}

Identifying Duplicate Errors

One last thing to mention is that when your stack trace arrives on the server it will contain file names and line numbers. The inconsistency of these numbers will make it difficult to identify duplicate errors. I suggest that you "clean" the stack traces by removing this extra information when trying to create a unique error hash.

private static readonly Regex LineCleaner
 = new Regex(@"\([^\)]+\)$", RegexOptions.Compiled);
private int GetUniqueHash(string[] stackTrace)
{
 var sb = new StringBuilder();
 foreach (var stackLine in stackTrace)
 {
 var cleanLine = LineCleaner
 .Replace(stackLine, String.Empty)
 .Trim();
 if (!String.IsNullOrWhiteSpace(cleanLine))
 sb.AppendLine(cleanLine);
 }
 return sb
 .ToString()
 .ToLowerInvariant()
 .GetHashCode();
}

Integration Steps

This article was meant to be more informational than tutorial; but if you are interested in trying to apply this to your site, here are the steps that you would need to take:

  1. Download JavaScriptErrorReporter from GitHub.
  2. Include StackTrace.js as a resource in your website.
  3. Include ErrorReporter.js as a resource in your website.
    • Again, to prevent it interfering with your JavaScript debugger, I suggest only including this resource when your scripts are being minified.
  4. Add a report error action to an appropriate controller. (Use the ReportError action on the HomeController as an example.)
  5. Add a "data-report-url" attribute with the fully qualified path to your report error action to the body tag of your pages.
  6. Log any errors that your site reports!
Shout it

Enjoy,
Tom

Sunday, February 17, 2013

Cache Repository for MVC

First and foremost, the CacheRepository is NOT web specific!

The CacheRepository library contains the abstract CacheRepositoryBase class, which can be implemented by any caching system that you choose. The CacheRepository.Web NuGet package includes a WebCacheRepository implementation that leverages System.Web.Caching.Cache

CacheRepository.Web

Get all of the source code, unit tests, and a complete MVC4 sample application (preconfigured to include dependency injection with Unity) from GitHub. Or to just jump in and start using it, grab the CacheRepository.Web package from NuGet.

ICacheRepository

The ICacheRepository interface has all the standard Get, Set, Remove and Clear methods. Additionally, these methods have been expanded to include enum parameters to group manage cache expiration.

Best of all, it includes GetOrSet methods. These methods will try to get the value of the specified key, and when it can not find that value it will then be loaded via the passed in Func. A key feature here is the fact that the setter of the GetOrSet will lock on load to prevent redundant data loads. (More details about this below.)

public interface ICacheRepository
{
 object Get(string key);
 T Get<T>(string key);
 T GetOrSet<T>(string key, Func<T> loader);
 T GetOrSet<T>(string key, Func<T> loader, DateTime expiration);
 T GetOrSet<T>(string key, Func<T> loader, TimeSpan sliding);
 T GetOrSet<T>(string key, Func<T> loader, CacheExpiration expiration);
 T GetOrSet<T>(string key, Func<T> loader, CacheSliding sliding);
 void Set<T>(string key, T value);
 void Set<T>(string key, T value, DateTime expiration);
 void Set<T>(string key, T value, TimeSpan sliding);
 void Set<T>(string key, T value, CacheExpiration expiration);
 void Set<T>(string key, T value, CacheSliding sliding);
 void Remove(string key);
 void ClearAll();
}

ThreadSafe GetOrSet

As any good cache should, the CacheRepository is thread safe and can be treated as a singleton.

Even better, the GetOrSet methods lock on set. This means that 10 threads could be trying to load the same cache value simultaneously, but only one will actually trigger a load and set. This helps redundant resource loads and database calls and keep your application running optimally.

private T GetOrSet<T>(string key, Func<T> loader,
 DateTime? expiration, TimeSpan? sliding)
{
 // Get It
 T value;
 var success = TryGet(key, out value);
 // Got It or No Loader
 if (loader == null || success)
 return value;
 // Load It
 return LockedInvoke(key, () =>
 {
 // Get It (Again)
 success = TryGet(key, out value);
 if (!success)
 {
 // Load It (For Real)
 value = loader();
 // Set It
 if (value != null)
 Set(key, value, expiration, sliding);
 }
 return value;
 });
}

Configurable Expiration Enums

Instead of being forced to set each of your cache expirations separately, you now have the option to use an enum value to group your cache expirations. Enums are available for both absolute expiration and sliding expiration.

You may override the duration of each cache key in your app config; this is really useful for when you want to uniformly modify your cache expirations in your QA or testing environments. Additionally, you may override the CacheRepositoryBase.GetConfigurationValue method to pull this configuration for anywhere you want, not just the ConfigurationManager.

public enum CacheExpiration
{
 VeryShort = 10, // Ten Seconds
 Short = 60, // One Minute
 Medium = 300, // Five Minutes
 Long = 3600, // One Hour
 VeryLong = 86400 // One Day
}

<configuration>
 <appSettings>
 <add key="CacheExpiration.VeryShort" value="15" />
 <add key="CacheExpiration.Short" value="90" />

ByType Extensions

Transparent cache key management is offered through a series of ICacheRepository extension methods. This makes caching even easier, as you don't need to worry about cache key collisions.

Using these extension methods, you many cache a Cat object with an Id of 1 and a Dog object with an Id of 1 in the same manner. The extension methods will create a key prefix based on the type and then use the specified identified as the suffix.

[Fact]
public void SameKeyDifferentType()
{
 var setCat = new Cat { Name = "Linq" };
 CacheRepository.SetByType(1, setCat);
 var setDog = new Dog { Name = "Taboo" };
 CacheRepository.SetByType(1, setDog);
 var getCat = CacheRepository.GetByType<Cat>(1);
 Assert.Equal(setCat.Name, getCat.Name);
 var getDog = CacheRepository.GetByType<Dog>(1);
 Assert.Equal(setDog.Name, getDog.Name);
}

Web Implementation & Dependency Injection via Unity

The CacheRepository.Web NuGet package includes a WebCacheRepository implementation that leverages System.Web.Caching.Cache. This is a very simple, but very effective, production ready implementation of the CacheRepositoryBase.

It should also be noted that this implementation is not only useful in ASP.NET, the System.Web.Caching.Cache is located in the System.Web library, but it still available outside of a web context. This means that this implementation can be used in other applications, including but not limited to Windows Services.

public class WebCacheRepository : CacheRepositoryBase
{
 private readonly Cache _cache;
 public WebCacheRepository()
 {
 _cache = HttpContext.Current == null
 ? HttpRuntime.Cache
 : HttpContext.Current.Cache;
 }

Wiring the cache repository to be injected into your controllers via Unity is also very easy. Just register the WebCacheRepository for the ICacheRepository interface, and be sure to provide a ContainerControlledLifetimeManager for optimal performance.

To use the implementation below, you will need to include the Unity.MVC3 NuGet Package to gain access to the UnityDependencyResolver.

public static void Initialise()
{
 var container = BuildUnityContainer();
 DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static IUnityContainer BuildUnityContainer()
{
 var container = new UnityContainer();
 container.RegisterType<ICacheRepository, WebCacheRepository>(
 new ContainerControlledLifetimeManager()); 
 return container;
}

If you missed the links above, here they are again:

Shout it

Enjoy,
Tom

Sunday, January 13, 2013

WebElement Form Extensions for WebDriver

I really like Selenium 2.0!

I've been working a lot on a side project involving WebDriver, and thus I have been creating a few extension methods for working with form data that I thought others might find helpful:

  • bool IsSelected(this IWebElement element)
  • bool IsChecked(this IWebElement element)
  • void SetChecked(this IWebElement element)
  • void SetUnchecked(this IWebElement element)
  • IWebElement GetOptionByValue(this IWebElement element, string value)
  • IWebElement GetOptionByText(this IWebElement element, string text)

Extension Methods

public static class WebElementExtensions
{
 private static readonly StringComparer DefaultComparer =
 StringComparer.InvariantCultureIgnoreCase;
 private static readonly string[] Selected = new[] { "true", "selected" };
 public static bool IsSelected(this IWebElement element)
 {
 var attribute = element.GetAttribute("selected");
 return Selected.Contains(attribute, DefaultComparer);
 }
 private static readonly string[] Checked = new[] { "true", "checked" };
 public static bool IsChecked(this IWebElement element)
 {
 var attribute = element.GetAttribute("checked");
 return Checked.Contains(attribute, DefaultComparer);
 }
 public static void SetChecked(this IWebElement element)
 {
 if (!element.IsChecked())
 element.Click();
 }
 public static void SetUnchecked(this IWebElement element)
 {
 if (element.IsChecked())
 element.Click();
 }
 private const StringComparison DefaultComparison = 
 StringComparison.InvariantCultureIgnoreCase;
 public static IWebElement GetOptionByValue(
 this IWebElement element, 
 string value)
 {
 return element.GetOption(
 o => value.Equals(o.GetAttribute("value"), DefaultComparison));
 }
 public static IWebElement GetOptionByText(
 this IWebElement element, 
 string text)
 {
 return element.GetOption(o => text.Equals(o.Text, DefaultComparison));
 }
 private static IWebElement GetOption(
 this IWebElement element, 
 Func<IWebElement, bool> predicate)
 {
 return element
 .FindElements(By.CssSelector("option"))
 .FirstOrDefault(predicate);
 }
}

Unit Tests

public class WebElementExtensionTests : IDisposable
{
 public IWebDriver Driver { get; private set; }
 public WebElementExtensionTests()
 {
 Driver = new ChromeDriver("C:/Code/Drivers");
 Driver.Url = "file:///C:/Code/WebDriverTests/TestPage.htm";
 }
 public void Dispose()
 {
 Driver.Dispose();
 }
 [Fact]
 public void RadioTests()
 {
 var e1 = Driver.FindElement(By.Id("r1"));
 var e2 = Driver.FindElement(By.Id("r2"));
 IsNotSelected(e1);
 IsSelected(e2);
 e1.Click();
 IsSelected(e1);
 e2.Click();
 IsNotSelected(e1);
 }
 protected void IsSelected(IWebElement element)
 {
 Assert.True(element.IsSelected());
 }
 protected void IsNotSelected(IWebElement element)
 {
 Assert.False(element.IsSelected());
 }
 [Fact]
 public void CheckboxTests()
 {
 var e1 = Driver.FindElement(By.Id("c1"));
 var e2 = Driver.FindElement(By.Id("c2"));
 IsNotChecked(e1);
 IsChecked(e2);
 e1.Click();
 IsChecked(e1);
 e1.Click();
 IsNotChecked(e1);
 }
 protected void IsChecked(IWebElement element)
 {
 Assert.True(element.IsChecked());
 }
 protected void IsNotChecked(IWebElement element)
 {
 Assert.False(element.IsChecked());
 }
 [Fact]
 public void OptionTests()
 {
 var e1 = Driver.FindElement(By.Id("s1"));
 var option1 = e1.GetOptionByValue("a");
 var option2 = e1.GetOptionByValue("b");
 IsNotSelected(option1);
 IsSelected(option2);
 option1.Click();
 IsSelected(option1);
 IsNotSelected(option2);
 }
 [Fact]
 public void ToggleCheckedTests()
 {
 var e1 = Driver.FindElement(By.Id("c1"));
 IsNotChecked(e1);
 e1.SetChecked();
 IsChecked(e1);
 e1.SetChecked();
 IsChecked(e1);
 e1.SetUnchecked();
 IsNotChecked(e1);
 e1.SetUnchecked();
 IsNotChecked(e1);
 }
}

TestPage.htm

<html><body><p><select id="s1"><option value="a">Ant</option><option value="b" selected="selected">Bird</option></select></p><p><input id="r1" type="radio" name="r" value="c"/>Cat<br><input id="r2" type="radio" name="r" value="d" checked="checked"/>Dog </p><p><input id="c1" type="checkbox" name="vehicle" value="e"/>Elephant<br><input id="c2" type="checkbox" name="vehicle" value="f" checked="checked"/>Fox </p></body></html>
Shout it

Enjoy,
Tom

Monday, October 22, 2012

How to Unlock a Configuration Section from IIS Manager

HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.

This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false".

If you have ever experienced this problem then you probably have also experienced the fun of digging through your system configuration files to find where to unlock the related authentication sections. This is, to say the least, not fun.

Did you know that you can unlock configuration sections from IIS Manager?

  1. Launch IIS Manager.
  2. Select your Connection's home page.
  3. Open the Configuration Editor under Management.
  4. Navigate to the section that you need to unlock.
  5. Look to the right Action pane and click unlock!

Shout it

Enjoy,
Tom

Monday, August 10, 2009

MVC JSON Model Binder Attribute

Here it is folks: the Ultimate Ultimate* ASP.NET MVC JSON Model Binder!

Moving complex data structures from client to server used to be difficult, but not anymore! Just add the JsonBinder attribute to your action parameters, and this Custom ModelBinder will automatically detect the type and parameter name, and deserialize your complex JSON object to the data structure of your choice.

No configuration required, it works every time, it's PFM!**

* Yes, I said Ultimate twice.
** Pure Friendly Magic

JsonBinderAttribute

public class JsonBinderAttribute : CustomModelBinderAttribute
{
 public override IModelBinder GetBinder()
 {
 return new JsonModelBinder();
 }
 public class JsonModelBinder : IModelBinder
 {
 public object BindModel(
 ControllerContext controllerContext, 
 ModelBindingContext bindingContext)
 {
 try
 {
 var json = controllerContext.HttpContext.Request
 .Params[bindingContext.ModelName];
 if (String.IsNullOrWhitespace(json))
 return null;
 // Swap this out with whichever Json deserializer you prefer.
 return Newtonsoft.Json.JsonConvert
 .DeserializeObject(json, bindingContext.ModelType);
 }
 catch
 {
 return null;
 }
 }
 }
}

Controller Action

public class PersonController : Controller
{
 // Note: The JsonBinder attribute has been added to the person parameter.
 [HttpPost]
 public ActionResult Update([JsonBinder]Person person)
 {
 // Both the person and its internal pet object have been populated!
 ViewData["PetName"] = person.Pet.Name;
 return View();
 }
}

Sample Models

public class Person
{
 public string Name { get; set; }
 public int Age { get; set; }
 // Note: This property is not a primitive!
 public DomesticAnimal Pet { get; set; }
}
public class DomesticAnimal
{
 public string Name { get; set; }
 public int Age { get; set; }
 public string Species { get; set; }
}

jQuery Method

function sendToServer() {
 var person = {
 Name : 'Tom',
 Age : 27,
 Pet : {
 Name : 'Taboo',
 Age : 2,
 Species : 'Shiba Inu'
 }
 };
 // {"Name":"Tom","Age":27,Pet:{"Name":"Taboo",Age:2,Species:"Shiba Inu"}}
 var personJson = JSON.stringify(person);
 $.ajax({
 url: 'Person/Update', 
 type: 'POST',
 data: { 
 person: personJson 
 }
 });
}
Shout it

Enjoy,
Tom

Subscribe to: Posts (Atom)

AltStyle によって変換されたページ (->オリジナル) /