What's the correct way to use Effort with Entity Framework 6?


Question

I'm trying to get some test code working using the Effort data provider with Entity Framework 6. What I'm trying to do seems like it should be the absolute simplest use-case, but I'm just not able to get things to work.

Here is my DbContext class:

public class CcdReductionFrameCatalogue : DbContext
    {
    public CcdReductionFrameCatalogue()
        : this("name=CcdReductionFrameCatalogue") {}

    public CcdReductionFrameCatalogue(string connectionString) : base(connectionString) {}

    public CcdReductionFrameCatalogue(DbConnection connection) : base(connection, true) {}

    public virtual DbSet<CcdFrame> CcdFrames { get; set; }
    }

The POCO entity is defined as:

public class CcdFrame : IEquatable<CcdFrame>
    {
    public CcdFrame()
        {
        AcquisitionTimeUtc = DateTime.UtcNow;
        }

    public int Id { get; set; }
    public string Location { get; set; }
    [FitsKeyword("INSTRUME")] public string CameraName { get; set; }

    [FitsKeyword("EXPTIME")] public double ExposureTimeSeconds { get; set; }

    [FitsKeyword("SET-TEMP"), FitsKeyword("CCD-TEMP")] public double TemperatureSetpoint { get; set; }

    [FitsKeyword("NAXIS1")] public int SizeX { get; set; }

    [FitsKeyword("NAXIS2")] public int SizeY { get; set; }

    [FitsKeyword("XBINNING")] public int BinningX { get; set; }

    [FitsKeyword("YBINNING")] public int BinningY { get; set; }

    [FitsKeyword("IMAGETYP")] public string FrameType { get; set; }

    [FitsKeyword("DATE-OBS")] public DateTime AcquisitionTimeUtc { get; set; }
    [FitsKeyword("XORGSUBF")] public int SubframeOriginX { get; set; }
    [FitsKeyword("YORGSUBF")] public int SubframeOriginY { get; set; }

    // IEquatable implementation elided for clarity
    }

The [FitsKeyword] attribute is a custom attribute that I've defined and should not have any bearing on Entity Framework.

In my unit tests, I set up my data connection like this, as shown in the Effort quick-start guide:

        Connection = DbConnectionFactory.CreateTransient();
        Repository = new CcdReductionFrameCatalogue(Connection);

As soon as I use any LINQ on the DbSet, I get stupid meaningless error messages. For example, when I pass my repository into this trivial code:

    static void AddOrUpdate(CcdFrame newFrame, CcdReductionFrameCatalogue repository)
        {
        var existingFrames = from frame in repository.CcdFrames
                             where frame.Equals(newFrame)
                             select frame;
        Console.WriteLine(existingFrames.Count());
        // ...never gets past here

When I run this code, I get:

System.NotSupportedExceptionUnable to create a constant value of type 'TA.ReductionManager.DomainObjects.CcdFrame'. Only primitive types or enumeration types are supported in this context.
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, ref DbExpression source, ref DbExpressionBinding sourceBinding, ref DbExpression lambda)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.AggregateTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.c__DisplayClass7.b__6()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction(Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.c__DisplayClass7.b__5()
   at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1..GetEnumerator>b__0()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Linq.Enumerable.Single(IEnumerable`1 source)
   at System.Linq.Queryable.Count(IQueryable`1 source)
   at TA.ReductionManager.Specifications.FitsImporter.AddOrUpdate(CcdFrame newFrame, CcdReductionFrameCatalogue repository) in FitsImporter.cs: line 38
   at TA.ReductionManager.Specifications.FitsImporter.ImportCollection(IEnumerable`1 collection, CcdReductionFrameCatalogue repository) in FitsImporter.cs: line 29
   at TA.ReductionManager.Specifications.when_importing_fits_files_into_the_catalogue_and_there_are_no_subdirectories.b__5() in FileEnumeratorSpecs.cs: line 27

Now this is mind-numbingly simple LINQ code, what am I missing here?

Accepted Answer

You cannot compare custom objects in a LINQ query. You should compare only primitive type (int, string, etc).

var existingFrames = from frame in repository.CcdFrames
                     where frame.Id == newFrame.Id
                     select frame;

You can get some info here: Only primitive types or enumeration types are supported in this context





Licensed under: CC-BY-SA
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why