Using AutoFixture with Entity Framework

Jun 23, 2011 at 7:35 PM

I have large number of tests that generate Entity Framework 4.0 data. I thought AutoFixture could both simplify and enhance data initialization. So I replaced the following code:

            var programmes = new Programmes { ProgId = Guid.NewGuid().ToString(), Medium = medium};

with this one:

            var programmes = fixture.Build<Programmes>().With(x => x.Medium, medium).CreateAnonymous();

Programmes is a class auto-generated using EF from the existing database table.

First what I need to achieve is to limit the length of generated strings, so they won't exceed database column defined lengths. But there seem to be a more serious thing that happens. I am getting an exception, and the call stack indicates that there is an attempt to access the database during the data generation:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentException: The name 'Key5e62dd80-a28a-468b-8327-08482a8cacfa' contains characters that are not valid.
    at System.Data.EntityKey.CheckKeyValues(IEnumerable`1 entityKeyValues, Boolean allowNullKeys, Boolean tokenizeStrings, String[]& keyNames, Object& singletonKeyValue, Object[]& compositeKeyValues)
    at System.Data.EntityKey.set_EntityKeyValues(EntityKeyMember[] value)
     --- End of inner exception stack trace ---
    at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
    at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
<skipped>
    at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
    at Ploeh.AutoFixture.Kernel.AutoPropertiesCommand`1.Execute(T specimen, ISpecimenContext context)
    at Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(Object request, ISpecimenContext context)
    at Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.<>c__DisplayClass6.<Create>b__1(ISpecimenBuilder b)
    at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
    at System.Linq.Enumerable.<DefaultIfEmptyIterator>d__a5`1.MoveNext()
    at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
    at Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object request, ISpecimenContext context)
    at Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object request, ISpecimenContext context)
    at Ploeh.AutoFixture.Kernel.SpecimenContext.Resolve(Object request)
    at Ploeh.AutoFixture.Kernel.SeedIgnoringRelay.Create(Object request, ISpecimenContext context)
    at Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.<>c__DisplayClass6.<Create>b__1(ISpecimenBuilder b)
<skipped>
    at Ploeh.AutoFixture.Kernel.SpecimenContext.Resolve(Object request)
    at Ploeh.AutoFixture.SpecimenFactory.CreateAnonymous[T](ISpecimenContext context, T seed)
    at Ploeh.AutoFixture.SpecimenFactory.CreateAnonymous[T](ISpecimenContext context)
    at Ploeh.AutoFixture.SpecimenFactory.CreateAnonymous[T](ISpecimenBuilderComposer composer)

I am not sure why it's trying to call FirstOrDefault. Can this be because of related entities? If so, how this can be fixed?

Thanks in advance

Vagif

Coordinator
Jun 23, 2011 at 10:13 PM

EF is just evil and you'll probably be better off without it :)

It's totally expected behavior from AutoFixture that it will attempt to assign a value to a Key property if it's a writable property, and that the value might be something like "Key5e62dd80-a28a-468b-8327-08482a8cacfa". It's been a while since I last used EF, so I can't explain why it's trying to do that, but I assume that you can get around this issue by telling AutoFixture to ignore that property:

fixture.Build<Programmes>().With(x => x.Medium, medium).Without(x => x.Key).CreateAnonymous();

Jun 24, 2011 at 7:55 AM

Thanks, but I don't want to ignore the key, I just want to control it's length. So I suppose TypeMappings is the recommend way of solving this?

Jun 24, 2011 at 9:09 AM

I did more testing, and in fact the exception above was caused by other things.

1. Every EF4-generated entity has a property EntityKey, and AF tries to assign its value that results in exception similar to what I listed above. So first thing to add: Without(x => x.EntityKey)

2. Collections are no problem, but many-to-one links cause the same exception. My Programmes table had links to two other tables (Seasons and ExternalSystems), and EF generated the following properties:

ExternalSystems
ExternalSystemsReference
Seasons
SeasonsReference

And of course AF tried to initialize them that caused the same exception. So I had to extend the list of exclusions:

                .Without(x => x.ExternalSystems)
                .Without(x => x.ExternalSystemsReference)
                .Without(x => x.Seasons)
                .Without(x => x.SeasonsReference)

Then finally it worked, but such long list of exclusions more or less kills the whole point of auto-generating the entity properties. So I'll be better off with calling OmitAutoProperties and setting just the properties I need.

I am not sure if this situation can be improved. Of course AutoFixture can be customized to detect "special" types like those belonging to Entity Framework, so in principle all the above exclusions can be automated on AF level. I recently wrote an entity cloner that copies data between two EF-based sources, and I had to do a similar thing: detect EF-related properties and skip them. But such special treatment is only worth if there are many developers who use AF with EF, and it looks like I am one of very few. So for the time being I will settle for OmitAutoProperties.

Coordinator
Jun 25, 2011 at 11:54 AM

Addressing the issue about the key is probably the easiest thing to do. You could write a small convention-based customization that gives special treatment to Key properties for EF types. Here's an article that provides hints on how to do that: http://blog.ploeh.dk/2010/10/19/ConventionbasedCustomizationsWithAutoFixture.aspx

Regarding those 'navigation properties' the problem tends to be not many-to-one links but rather that they represent circular references. With AutoProperties enabled AutoFixture will attempt to assign a property, but if that type also has a property (or a list) with the original type, it will lead to an infinite recursion. This problem isn't specific to EF, but for all APIs that contain circular references. I consider this to be a design smell, so I've never wanted to do that much about it... it's also a more than trivial problem to solve in general, so AutoFixture just refuses to do that.

This doesn't mean that it's impossible to customize AutoFixture to understand EF-specific things. One might, for example, consider writing another convention-based ISpecimenBuilder that ignores those EF 'navigation properties'. If you can define an algorithm to detect them, it wouldn't be too hard to write a customization that ignores them.

It's probably possible to write a couple of general-purpose ISpecimenBuilders that address those concerns specifically for EF. They could be compiled into a separate assembly to provide an EF-specific extension to AutoFixture. It might even become an 'official' extension to AutoFixture - it is, after all, open source :)

Jun 27, 2011 at 8:46 AM

I am not sure I share your view of circular references as a design smell. In some cases it is, but how else could we model trees? But I agree it may not be core AutoFixture business to solve those issues, so it's fine to leave it to the customizations.

I will have a closer look at ISpecimenBuilder, I think your blog post describes it well. Later (after vacation) I may look into a general-purpose specimen builder that would handle EF.

Vagif