DevelopMENTAL Madness

Thursday, February 26, 2009

Unit Testing: Moq doesn’t support property indexers

I hit a road block today writing some tests which were dependent upon another class’s property indexer. When trying to do the following using Moq:

   1: [TestMethod]
   2: public void Test1()
   3: {
   4:     Mock<HttpContextBase> context = new Mock<HttpContextBase>();
   5:  
   6:     Object actual = null;
   7:  
   8:     context.ExpectSet(c => c.Session["SomeKey"]).Callback(o => actual = o);
   9:  
  10:     context.Object.Session["SomeKey"] = "some value";
  11:     
  12:     Assert.IsNotNull(actual, "Value was not set");
  13: }

Line 8 fails with this error:

System.ArgumentException: Expression is not a property access: c => c.Session.get_Item("SomeKey").

It appears the current stable build of Moq (version 2.6.1014.1) doesn’t support property indexers. The good news is that a few days ago support was added for property indexers. However, if you’re like me and you’re not working with the latest and greatest, then there’s a workaround. Just create your own mock implementation of the class with the indexed property. In my case the class in question was HttpSessionStateBase. Here’s my mock implementation:

/// <summary>
/// Mock implementation of HttpSessionStateBase
/// </summary>
/// <remarks>
/// Current stable build of Moq (2.6.1014.1) doesn't support property indexers 
/// at this time, so this is an implementation of HttpSessionStateBase 
/// which allows us to verify that the session cache is being updated.
/// </remarks>
internal class MockSession : HttpSessionStateBase
{
    Dictionary<string, object> session = new Dictionary<string, object>();
 
    public override void Add(string name, object value)
    {
        session.Add(name, value);
    }
 
    public override object this[string name]
    {
        get
        {
            if (!session.ContainsKey(name))
                return null;
 
            return session[name];
 
        }
        set
        {
            session[name] = value;
        }
    }
}

Then you can do the following:

// setup
MockSession session = new MockSession();
Mock<HttpContextBase> context = new Mock<HttpContextBase>();
context.ExpectGet(c => c.Session).Returns(session);
 
// exercise
context.Session["SomeKey"] = "some value";
 
// assert
Assert.IsNotNull(context.Session["SomeKey"]);

Happy Moqing!

Labels: ,

Wednesday, February 25, 2009

Entity Framework: Abstracting ObjectContext

Wither you are a TDD purest or not, if you are developing with Entity Framework and testability is important to you, then you have tried some workaround or applied some pattern to make testing possible in your project. When you have a class or method which depends on any external resource it is important to create some sort of abstraction in order to remove a dependency upon that resource so you can effectively test your code.

The client I am currently working for has chosen to create a repository class which takes an ObjectContext argument in the constructor. The repository implements a custom interface and that interface is used to reference the repository where needed.

While this meets our needs I was late to the game when I was brought on, so when I suggested an alternative I was praised for my suggestion but because there was already an implementation we ultimately stuck with what we already had. So I decided just to make a quick post describing my own solution and touching on what I see as the benefits.

I (heart) Partial Classes

My ideal framework is open, extensible and makes abundant use of public interfaces which allow me to hook in where I need to so I can get the job done. The team I would say has done the best job of this would be the ASP.NET MVC team. I’m a bit picky about my ORM solutions and I just don’t feel like LINQ2SQL and Entity Framework have quite done everything the way I like. And the ability to extend or change the behavior of Entity Framework seems to be pretty lacking. But there is one thing that I have found to be invaluable as I have worked with these two ORM frameworks – Partial Classes.

Open any *.designer.cs file generated by either LINQ2SQL or Entity Framework and you will see an abundant use of the C# keyword “partial”. While it doesn’t allow me to modify existing Entity Framework behavior I can certainly add to it.

Partial classes are what make it possible for me to simply and elegantly create an abstraction of my ObjectContext without bending over backwards.

The Abstraction

So as an example, I have an AdventureWorks database and I want to abstract the associated ObjectContext created by my edmx designer. To simplify things I’ll simply referece the Person and Address tables. So after finishing with my edmx file, dragging Person and Address to the designer surface I’ll create an interface which looks like this:

public interface ICustomRepository : IDisposable
{
    IQueryable<Person> People { get; }
    IQueryable<Address> Addresses { get; }
    
    Int32 SaveChanges();
 
    void Detach(Object entity);
    void Attach(IEntityWithKey entity);
 
    void DeleteObject(Object entity);
    void AddObject(String entitySetName, Object entity);
}

Because of the difficulty of implementing ObjectQuery<T>, I like to expose the entity set properties on my ObjectContext as IQueryable<T>. In most cases this is sufficient, where it isn’t I can check to see if the IQueryable is of type ObjectQuery. If not then I know I’m in a testing scenario and I can safely skip what I was about to do. Here’s an example of an extension method I’ve created which allows me to use eager loading on my IQuerable interface.

public static IQueryable<T> Include<T>(this IQueryable<T> query, string path)
{
    ObjectQuery<T> q = query as ObjectQuery<T>;
    if (q == null)
        return query;
 
    q = q.Include(path);
 
    return q;
}

Once I’ve added a read-only (property get) for each entity set in my context, then I include the signature for each ObjectContext method I will be using in my application. If I need additional ones down the road, all I need to do is add the signature to the interface and I’m off and running. So I’ve included SaveChanges, Detach, Attach, DeleteObject and AddObject. Feel free to include others you need they’re free and you don’t need to provide any implementation.

The Implementation

Now that we have an interface that can be used throughout our application in place of our ObjectContext, how do we use it to remove our dependency on ObjectContext? Let’s say for argument’s sake our ObjectContext is named AdventureWorksModel. Then we would add a class file to our project which contains our edmx file and in that class file we would define a new class like this:

public partial class AdventureWorksModel : ICustomRepository
{
    IQueryable<Person> ICustomRepository.People 
    { 
        get 
        { 
            return this.People; 
        } 
    }
    IQueryable<Address> ICustomRepository.Addresses 
    { 
        get
        { 
            return this.Addresses; 
        } 
    }
}

First, notice the property names (ex. ICustomRepository.People). Because the properties exposed by the designer generated class are of the type ObjectQuery we have to provide an “explicit implementation” of our interface for those properties. No casting is required, but the compiler and the runtime are looking for properties of type IQueryable and so unless you include the name of the interface in the property name it won’t compile. But because ObjectQuery implements IQueryable, that’s all you have to do. Now you can reference ICustomRepository anywhere and you’re good to go.

Usage

There are any number of ways you can provide access to your repository. The easiest, in my view is using an IoC container like Unity or Spring. Here’s an example of using Unity to register your ObjectContext and resolving it from your container:

// put this in your application's startup event
Container.Register<ICustomRepository, AdventureWorksModel>();
 
// get a reference to your repository anywhere in your application
using(ICustomRepository repository = Container.Resolve<ICustomRepository>())
{
    var result = from p in repository.People
                    where p.Addresses.Any( a => a.State = "CA" )
                    select p;
    
    // do something
}

Once you’ve registered your ObjectContext in the container your application can get a reference to it anywhere through the custom interface. Everything remains testable and there’s no need for any awkward machinations to use it.

Mocking the Abstraction

Now when you’re testing you can write your own mock objects or use your favorite mocking framework (I really like MOQ) to provide an implementation of your interface that isn’t dependent upon your database.

[TestClass]
public class DependentObjectTest
{
    ICustomRepository _repository;
    
    [TestInitialize]
    public void Init()
    {
        List<Person> people = new List<Person>();
        List<Address> addresses = new List<Address>();
        
        Moq.Mock mock = new Mock<ICustomRepository>();
        mock.ExpectGet(r => r.People).Returns(people);
        mock.ExpectGet(r => r.Addresses).Returns(addresses);
        mock.Expect(r => r.AddObject("People", It.IsAny<Object>()))
            .Callback<string, Person>((s, p) => people.Add(p));
        mock.Expect(r => r.AddObject("Addresses", It.IsAny<Object>()))
            .Callback<string, Person>((s, a) => addresses.Add(a));
            
        _repository = mock.Object;
    }
    
    [TestMethod]
    public void Test1()
    {
        Person p = new Person();
        _repository.AddObject("People", p);
        Assert.AreEqual<int>(1, _repository.People.Count);
    }
}

While this isn’t a realistic test scenario because it is only testing the mock object, it should give you an idea of how to mock up an implementation of the interface for use in a test scenario.

If you’re using Unity or something like it then in your TestInitialize method you’ll just change the last line to this:

Container.Register<ICustomRepository, AdventureWorksModel>();

Then your test method won’t need to reference it since the methods you’re testing will get the reference via the Container object.

Conclusion

Thus far, I have found this is a simple and elegant solution which works just like using ObjectContext in any scenario.

Labels: , ,

Tuesday, February 24, 2009

Entity Framework: How to Prevent Eager Loading

As we have been using Entity Framework on my current project I have found that it can be easy to kill eager loading on your queries. This can be a real problem if you are counting on it. I have been working on cross-cutting concerns up to this point, but because of my experience with Linq 2 SQL I was asked about some issues our team was having with eager loading. Namely, the Include() method, which is a method of the ObjectQuery type. If you’ve read any documentation on eager loading I’m sure you’ve read that using projections precludes the use of the Include method. That is you can use it, but it will be ignored (no errors), and you will be left scratching your head. I found we were doing two things which were killing eager loading, which I believe are tied to projections.

Eager Loading

Before I mention the 3 issues I’ve seen to this point, I wanted to preface the list with a summary of how eager loading works.

In order to get the benefit of eager loading you need to, at minimum, define an association between your entities. This is done either in the designer or in your csdl (class structure element of your entity definition file (edmx). When you first create your model, you get this for free if you have defined foreign key constraints in your data model (RDBMS). If you have then the designer will automatically add navigation properties for each entity and an association between the two.

If you want Entity Framework to eager load related entities then you simply call the Include method of a ObjectQuery instance and pass in the name of the NavigationProperty for the association you want included:

using(AdventureWorksModel ctx = new AdventureWorksModel())
{
    var results = from p in ctx.Persons.Include("Addresses")
                    select p;
                    
    List<Person> customers = results.ToList();
}

Once you execute your query Entity Framework all the data, including the data for the association, will be loaded into a collection for you. When you reference the navigation property of an entity for the specified association you’ll find the data is there waiting for you. As long as the Navigation Property exists and you’ve typed the name correctly this will work.

Eager Loading Anti-Patterns

So what can get in the way of eager loading? A couple things:

  1. As I mentioned above, projections do not support eager loading. I believe this is at the root of the issue if you’re experiencing problems with eager loading. Why don’t projections allow for eager loading? For the simple reason that projections create anonymous types. If you aren’t projecting an anonymous type then it’s because the type you are projecting to isn’t in your entity model. Your entity model includes ssdl (storage definition) and mdl (mapping definition) which allows your entity to be mapped to a SQL query. Without these pieces you get nothing. If you need to do a projection of some sort, but you want to include eager loading then see this article which uses projection to include the related data as properties of the projection.
  2. The first thing I ran into was casting to a subclass. With Entity Framework you can mark an entity as an abstract class and inherit from it. Then our LINQ query was casting the “select” clause to a subsclass like this:
    var result = from ps in EntityModel.Persons.IncludeGraph("Addresses")
                  where ps is Customer
                  select ps as Customer;

    I haven’t figured out yet what this is doing under the covers, but I’m guessing this is resulting in a projection, if not something very close to one. Here I am querying an EntitySet of type Person for a Customer type. While the Entity Framework supports inheritance like this, I can see how this would be treated like a projection because of the conversion required. Instead of doing this, I’ve found you can work around the issue by refraining from using the cast in the select statement. Instead cast the result of your query after it has been materialized.

  3. Joining two tables in a query will kill eager loading. It would seem to me that the reason this breaks is because when you join like this it is unreliable. If you were to run this query against a one-to-one relationship you’d be fine. But in any other scenario this breaks down because unless you’re using the DISTINCT clause, things get sloppy.
    var result = from ps in EntityModel.Persons.IncludeGraph("Addresses")
                    join a in EntityModel.Addresses on ps.PersonID equals a.PersonID
                  where ps is Customer && a.State == "CA"
                  select ps;
     

    So Unless you need to create a projection with the results, in which case you can’t use eager loading anyway, then try and use subquerys (Any()).

    var result = from ps in EntityModel.Persons.IncludeGraph("Addresses")
                  where ps is Customer && ps.Addresses.Any( a => a.State == "CA" )
                  select ps as Customer;

Conclusion

So to recap, make sure you have defined navigation properties where you want to enable eager loading and that your arguments to the Include method are spelled the same as your navigation properties. Then avoid anything that Entity Framework would construe as an implicit (or explicit) projection. There may well be other reasons eager loading breaks down but this should be a start.

Labels:

Tuesday, February 10, 2009

Entity Framework: Connection Strings

<add name="AdventureWorksEntities" 
    connectionString="metadata=.\AdventureWorks.csdl|.\AdventureWorks.ssdl|.\AdventureWorks.msl;
    provider=System.Data.SqlClient;provider connection string='Data Source=localhost;
    Initial Catalog=AdventureWorks;Integrated Security=True;Connection Timeout=60;
    multipleactiveresultsets=true'" providerName="System.Data.EntityClient" />

According to MSDN, the above EntityClient connection string will search in the following locations for the specified csdl, ssdl and msl resource files:

  1. The calling assembly
  2. The referenced assemblies
  3. The assemblies in the bin directory of an application

However, I have not found this to be the case. When my edmx file is in an assembly which is external to my project, for example a test project or web project, I get the following error:

System.Data.MetadataException: The specified metadata path is not valid. A valid path must be either an existing directory, an existing file with extension '.csdl', '.ssdl', or '.msl', or a URI that identifies an embedded resource.

And the stack trace includes the following path:

System.Data.Metadata.Edm.MetadataArtifactLoader.Create(String path, ExtensionCheck extensionCheck, String validExtension, ICollection`1 uriRegistry, MetadataArtifactAssemblyResolver resolver)
System.Data.EntityClient.EntityConnection.SplitPaths(String paths)
System.Data.EntityClient.EntityConnection.GetMetadataWorkspace(Boolean initializeAllCollections)
System.Data.Objects.ObjectContext.RetrieveMetadataWorkspaceFromConnection()
System.Data.Objects.ObjectContext..ctor(EntityConnection connection, Boolean isConnectionConstructor)
System.Data.Objects.ObjectContext..ctor(String connectionString, String defaultContainerName)

Fortunately, the documentation also includes the syntax for explicitly referencing the resource files located in an external assembly. I’ve played around with a few different versions of this and this is the minimum information required in an EntityClient connection string:

<add name="AdventureWorksEntities" providerName="System.Data.EntityClient" 
    connectionString="metadata=res://MyCompany.Data,Culture=neutral,PublicKeyToken=null/Models.AdventureWorks.csdl
    |res://MyCompany.Data,Culture=neutral,PublicKeyToken=null/Models.AdventureWorks.ssdl
    |res://MyCompany.Data,Culture=neutral,PublicKeyToken=null/Models.AdventureWorks.msl; 
    provider=System.Data.SqlClient;provider connection string='Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=True;multipleactiveresultsets=true'"/>

This connection string assumes your assembly is named MyCompany.Data.dll and the full name of your class is MyCompany.Data.Models.AdventureWorks.

I’m sure I’ll need this myself in the future. I hope it helps you as well.

Labels: ,