4

Since I have switched to .net 4.5 and EF 5.0 I have started using enums mapping. In my project I'm using model first. Because all of enums were declared earlier so I have decided to user option "Reference external type" while I was converting fields to enums in EDM. Everything works fine but when I'm trying to execute code like

public enum SomeEnum : int
{
    value1 = 0,
    value2 = 1
}

class Program
{
    static void Main(string[] args)
    {
        TestDbEntities context = new TestDbEntities();
        var enumList = new List<SomeEnum>() { SomeEnum.value1, SomeEnum.value2 };
        var items = context.Table1.Where(e => enumList.Contains(e.EnumField));
        foreach (var item in items)
        {
            Console.Write(item.Id);
        }
        context.Dispose();
    }
}

I'm getting ArgumentException with message:

The type 'SomeEnum' does not match the EDM enumeration type 'SomeEnum' or its underlying type 'Int32' Parameter name: value.    

What is wired is that when I'm not using reference external type everything is okay. I'm understand what is behind in EDM, but I cannot understand why this code is working when the enum is defined in model and later generated by T4

  <EntityContainer Name="TestDbEntities" p1:LazyLoadingEnabled="true">
      <EntitySet Name="Table1" EntityType="TestDbModel.Table1" />
    </EntityContainer>
    <EntityType Name="Table1">
      <Key>
        <PropertyRef Name="Id" />
        <PropertyRef Name="EnumField" />
      </Key>
      <Property Name="Id" Type="Int32" Nullable="false" />
      <Property Name="EnumField" Type="TestDbModel.SomeEnum" Nullable="false" />
    </EntityType>
    <EnumType Name="SomeEnum" a:ExternalTypeName="ConsoleApplication1.SomeEnum" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

I would like to use reference external type but this is some limitation. I know that I can cast to underlying type, but this is a hack.

----edit Here is a stack trace

at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateConstant(TypeUsage constantType, Object value)
at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.NewArrayInitTranslator.<>c__DisplayClass88.<TypedTranslate>b__86(Expression e)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.EnumerableValidator`3.Validate(IEnumerable`1 argument, String argumentName, Int32 expectedElementCount, Boolean allowEmpty, Func`3 map, Func`2 collect, Func`3 deriveName)
at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.EnumerableValidator`3.Validate()
at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.CreateExpressionList(IEnumerable`1 arguments, String argumentName, Boolean allowEmpty, Action`2 validationCallback)
at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateNewCollection(IEnumerable`1 elements, DbExpressionList& validElements)
at System.Data.Common.CommandTrees.ExpressionBuilder.DbExpressionBuilder.CreateNewCollection(IEnumerable`1 elements)
at System.Data.Objects.ELinq.ExpressionConverter.NewArrayInitTranslator.TypedTranslate(ExpressionConverter parent, NewArrayExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.ContainsTranslator.TranslateContains(ExpressionConverter parent, Expression sourceExpression, Expression valueExpression)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.ContainsTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.Convert()
at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Data.Entity.Internal.Linq.InternalQuery`1.GetEnumerator()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()
at ConsoleApplication1.Program.Main(String[] args) in c:\Users\Pawel\Documents\Visual Studio 2012\Projects\ClassLibrary1\ConsoleApplication1\Program.cs:line 22
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Paweł Madurski
  • 121
  • 1
  • 4
  • Can you post the full stacktrace? Can you replace `enumList.Contains(e.EnumField))` with `e.EnumField == SomeEnum.value1 || e.EnumField == SomeEnum.value2` and let me know if it works. Another thing I would like you to check is if it works if you add CLR Type enum members to the CSDL Enum Type. I suspect there might be a bug here. – Pawel Oct 25 '12 at 04:14
  • To replicate my problem you can simply create some EDM model and then convert some int to enum using "Reference external type" option. The solution: e.EnumField == SomeEnum.value1 || e.EnumField == SomeEnum.value2 works, but since I have refactor existing code to have enums it will be hard to find all places. Mapping CLR Type enum members to the CSDL Enum Type works like a charm as I mention before – Paweł Madurski Oct 25 '12 at 07:10
  • Updated my post about enums in EF5 to reflect this bug. – Pawel Nov 10 '12 at 07:52

2 Answers2

4

This is a bug. I filed it on the EntityFramework Codeplex site: http://entityframework.codeplex.com/workitem/623. For now the workaround is to either use || or specify all the members you have in the CLR enum type in the EDM enum type. Setting external enum type adds an annotation to the EDM enum type (a:ExternalTypeName="ConsoleApplication1.SomeEnum") that tells code gen to not generate this type but use the value of the attribute everywhere in the generated code where the type is used. Without this annotation a CLR enum that exactly matches the EDM enum type will be generated. Btw. I wrote a blogpost on EF5 and external enum types some time ago. You can find it here: http://blog.3d-logic.com/2012/09/11/using-exisiting-enum-types-in-entity-framework-5/. I will add information about the bug on the blog. Feel free to comment on it as well.

Pawel
  • 31,342
  • 4
  • 73
  • 104
  • So, in the current available version of EF6 you don't have to specify all the enum values in the edmx? – Martijn Sep 23 '13 at 14:57
  • 1
    In EF6 it is not necessary (in fact in EF5 it was only necessary if you have a query that hit the bug). An alternative is to use Code First and not to have to worry about edmx at all. – Pawel Sep 23 '13 at 17:30
2

Not a direct solution, but this workaround is working for my requirements.

var enumList = new List<int>() { (int)SomeEnum.value1, (int)SomeEnum.value2 }; 
var items = context.Table1.Where(e => enumList.Contains((int)e.EnumField)); 
alxx
  • 9,897
  • 4
  • 26
  • 41