Pablo Blamirez

UK based .Net dude

Friday, 25 September 2009

Learning XNA – Registering Services

 

Is it just me or the default syntax for registering/locating services a bit ugly. A couple of extension methods should help tidy that up a bit.

public static class GameExtensions
{
public static void AddService<I>(this Game game, I service) where I : class
{
game.Services.AddService(typeof(I), service);
}

public static I GetService<I>(this Game game) where I : class
{
return game.Services.GetService(typeof(I)) as I;
}
}

Friday, 10 October 2008

EntityFramework Transparent Lazy Load


Now I know there are other solutions to this scenario already, notably http://code.msdn.microsoft.com/EFLazyLoading

However I wanted to work with the standard generated code, as it is an assumption on my part that this code will be better tested.

My Solution

Use PostSharp to weave aspects into the code to perform the appropriate IsLoaded and Load calls on the RelatedEnd of the accessed association.

Warning:

As with any other lazy load solution, just because you can doesn't mean that you should. Lazy loading can kill performance.
Please treat the ability to lazy load as a convenience and optimize your returned spans appropriately

Implementation

1) Install PostSharp V1.0RTM http://www.postsharp.org/ (Please consider donating 50 euros to them if you end up using their tool on an ongoing basis)

2) Add the following references to your project

PostSharp.Public;
PostSharp.Laos;

3) Create a new .cs file containing the following code (Refactor to mutiple files if you want)

using System;
using PostSharp.Extensibility;
using PostSharp.Laos;
using System.Reflection;
using System.Data.Objects.DataClasses;
using System.Collections;

namespace YourNamespace
{
    [Serializable]
    public abstract class LazyLoadBase : OnMethodInvocationAspect
    {
        //Load the related end if we can
        protected void Load(RelatedEnd end)
        {
            if (end != null && !end.IsLoaded)
            {
                end.Load();
            }
        }
    }

    [Serializable]
    public class LazyLoadSetAttribute : LazyLoadBase
    {
        //Only add this aspect to properties that return an enumerable, RelatedEnd, i.e. EntityCollection
        public override bool CompileTimeValidate(MethodBase method)
        {
            MethodInfo info = method as MethodInfo;
            if (info == null) return false;
            bool isRelatedEnd = typeof(RelatedEnd).IsAssignableFrom(info.ReturnType);
            bool isSet = typeof(IEnumerable).IsAssignableFrom(info.ReturnType);
            return isSet && isRelatedEnd;
        }

        public override void OnInvocation(MethodInvocationEventArgs eventArgs)
        {
            eventArgs.Proceed();
            RelatedEnd end = eventArgs.ReturnValue as RelatedEnd;
            Load(end);
        }
    }

    [Serializable]
    public class LazyLoadRefAttribute : LazyLoadBase
    {
        //Only add this aspect to properties that return an EntityObject
        public override bool CompileTimeValidate(System.Reflection.MethodBase method)
        {
            MethodInfo info = method as MethodInfo;
            return typeof(EntityObject).IsAssignableFrom(info.ReturnType);
        }

        public override void OnInvocation(MethodInvocationEventArgs eventArgs)
        {
            RelatedEnd end = GetRelatedEnd(eventArgs.Delegate);
            Load(end);
            eventArgs.Proceed();
        }

        //Infer the name of the associated property that returns the EntityReference for the EntityObject, and force a load
        private System.Data.Objects.DataClasses.RelatedEnd GetRelatedEnd(Delegate currentMethod)
        {           
            string entityRefProperty = string.Format("{0}Reference", currentMethod.Method.Name);
            if (entityRefProperty.StartsWith("~get_")) entityRefProperty = entityRefProperty.Substring(5);
            PropertyInfo prop = currentMethod.Method.ReflectedType.GetProperty(entityRefProperty);
            if (prop == null) return null;
            return prop.GetValue(currentMethod.Target, null) as System.Data.Objects.DataClasses.RelatedEnd;
        }
    }

}

3) Add the following lines to your AssemblyInfo.cs

using YourNamespace;
[assembly: LazyLoadRefAttribute(AttributeTargetAssemblies = "YOUR_ASSEMBLY_NAME", AttributeTargetTypes = "EF_OBJECTCONTEXT_NAMESPACE.*")]
[assembly: LazyLoadSetAttribute(AttributeTargetAssemblies = "YOUR_ASSEMBLY_NAME", AttributeTargetTypes = "EF_OBJECTCONTEXT_NAMESPACE.*")]

Conclusion

And that's it, PostSharp will weave its magic when you build your project, leaving you free to focus on functionality first, optimization second.

Friday, 26 September 2008

Turn Off Visual Studio Outlining - AKA Regions


Menu: Tools -> Options

Treeview: Text Editor -> C# -> Advanced

Properties Heading: Outlining

Property: Enter outlining mode when files open. Untick

Now all regions will be expanded by default when you open some C# source

Tuesday, 20 May 2008

RouteData -> Controller.Action(params) validation


Following on from Phil Haack's excellent route evaluator for unit testing routes article, I got to wondering that if in addition to testing that for the presence of a RouteData match in our unit tests if we could also test that the invoked method and it's supplied parameters meet our expectations too.

To that end I have created an extension method for the RouteData class called VerifyAction which takes the generic parameter of the type of controller that we expect to be invoked and a lambda expression for a method call that returns an ActionResult value.

As with any extension method remember to include the namespace in your using clauses to make it available to the compiler

The specified lambda expression will take the parameters that should have been extracted from the virtualPath or supplied by the routes defaults.

An example is probably in order:

Example Unit Test

public void TestHomeControllerActionWithIdParam()
{
var routes = new RouteCollection();
GlobalApplication.RegisterRoutes(routes);

var evaluator = new RouteEvaluator(routes);
IList<RouteData> matchingRouteData = evaluator.GetMatches("~/Home/ActionWithIdParam/6");
Assert.IsTrue(matchingRouteData.Count > 0);
matchingRouteData[0].VerifyAction<HomeController>(c => c.ActionWithIdParam(6));
}


My expectation is that the url '~/Home/ActionWithIdParam/6' should invoke the ActionWithIdParam method found on HomeController passing in an Id of 6.



Validation Checks



The VerifyAction method checks the following aspects to determine if our expectations are met:




  • That the RouteData.Values["Controller"] value is equal to the Controller class supplied (minus the controller extension)


  • That the RouteData.Values["Action"] value is equal to the method name extracted from the lambda expression


  • That each argument of the action method has a corresponding entry in the RouteData.Values dictionary


  • That a null value is not supplied for an action method argument that expects a value type (eg int etc)


  • That each supplied argument value can be converted to the expected argument type


  • That each supplied argument value equals the expected argument value



If any of the checks fail an exception is thrown detailing the problem.



Feedback



Obviously that is what I intend that the code should do and hopefully it does :-) Seriously though when you find a bug, make an improvement or jump to the conclusion that the approach is flawed please can you post in the comments so that I can reap the benefits too, many thanks in advance.



The code for the extension method can be found below.



Thanks for stopping by,

Paul Blamire




RouteDataEvaluator Code



using System;
using System.Collections.Generic;
using System.Web.Routing;
using System.Web;
using System.Reflection;
using System.Linq.Expressions;

namespace MyNameSpace
{

public static class RouteDataEvaluator
{

public class RouteDataActionException : Exception
{
public RouteDataActionException(string message)
: base(message)
{

}
}

public static bool VerifyAction<CT>(this RouteData data, Expression<Func<CT, System.Web.Mvc.ActionResult>> expr) where CT : System.Web.Mvc.Controller
{

const string controllerKey = "Controller";
const string actionKey = "Action";

bool result = true;

LambdaExpression lambda = expr as LambdaExpression;
MethodCallExpression methodCall = lambda.Body as MethodCallExpression;

string cName = methodCall.Object.Type.Name;
string cNameLower = cName.ToLower();

if (SafeString(data.Values[controllerKey]) != ((cNameLower.EndsWith("controller")) ? cName.Substring(0, cName.Length - 10) : cName))
{
throw new RouteDataActionException(string.Format("Expected controller '{0}', matched '{1}'", cName, data.Values[controllerKey]));
}


if (SafeString(data.Values[actionKey]) != methodCall.Method.Name)
{
throw new RouteDataActionException(string.Format("Expected action '{0}', matched '{1}'", methodCall.Method.Name, data.Values[actionKey]));
}

ParameterInfo[] parameters = methodCall.Method.GetParameters();
for (int i = 0; i < methodCall.Arguments.Count; i++)
{
ConstantExpression param = methodCall.Arguments[i] as ConstantExpression;
string paramName = parameters[i].Name;
if (!data.Values.ContainsKey(paramName))
{
throw new RouteDataActionException(string.Format("Expected parameter '{0}', no matching parameter found", paramName));
}
else
{
string passedData = SafeString(data.Values[paramName]);
if (param.Type.IsValueType && passedData == null)
{
throw new RouteDataActionException(string.Format("Expected '{0}' parameter value of value type '{1}' , unable to convert from null", paramName, param.Type.Name));
}
object convertedData = null;
try
{
convertedData = Convert.ChangeType(passedData, param.Type);
if (!((convertedData == null && param.Value == null) || (convertedData != null && convertedData.Equals(param.Value))))
{
throw new RouteDataActionException(string.Format("Expected '{0}' parameter value of '{1}' , actual data '{2}'", paramName, param.Value, data.Values[paramName]));
}
}
catch (FormatException)
{
throw new RouteDataActionException(string.Format("Expected '{0}' parameter value of value type '{1}' , unable to convert from actual data '{2}'", paramName, param.Type.Name, data.Values[paramName]));
}
}
}

return result;

}

private static string SafeString(object value)
{
if (value == null)
{
return (string)null;
}
else
{
return value.ToString();
}
}
}
}

Friday, 29 June 2007

Monorail - graphs


Loving Castle Project's Monorail.  Having used webforms for the last 5 years I'm currently enjoying the freedom to generate whatever HTML I desire. For most of the applications I work on CRUD screens are the order of the day and as such the HTML is fairly straightforward.

Very occasionally though I miss some of the web-controls that are available. Graphing is an excellent example of that. Having briefly contemplated writing a server-side control to squirt out a jpeg/gif (couldn't find any 3rd party graphing controls to do it) a colleague of mine discovered AnyChart, a flash based graphing component.

Perfect

Castle Project - ActiveRecord and nHibernate

For anyone new to using Castle Projects excellent ActiveRecord implementation I would recommend the following book to gain a good grounding in (n)Hibernate.

Sometimes only the power of hsql will cut it...

http://www.manning.com/bauer/

About Me