Reputation: 4392
I have an ASP.NET application using an Entity Framework 6 model as the data access layer. It is in its own assembly.
In a separate assembly, I have a business logic layer which has many classes with names that overlap with the Entity model classes. The classes in this assembly are in a separate namespace from the DAL model.
So, for example:
DB Tables
Entity Framework DAL classes (Assembly: ProjNameDAL)
Business Logic Classes (Assembly: ProjNameBLL)
I didn't have any problems with this setup all the way through development and deployment. In fact, I've used this pattern in multiple projects. But, yesterday I made a schema change to the database, updated my DAL and BLL, and compiled my project. When I try to execute SOME (not all) BLL methods that use the DAL, I get the dreaded CLR/EDM ambiguous type mapping error:
The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type 'TableD'. Previously found CLR type 'OrgName.ProjName.DataAccess.TableD', newly found CLR type 'OrgName.ProjName.TableD'.
I get this message for 3 of the overlapping types, none of which are even used in the method in question.
If I change the BLL class names, of course this works around the issue. But, I'd rather not have to prefix/suffix my naming conventions if possible.
I have read up on the limitations with using multiple EF models, but the conditions for that don't seem to directly apply here. Plus, I have had nearly identical configurations working well in the past. Can anyone explain this runtime error and how I might avoid it?
Upvotes: 3
Views: 5670
Reputation: 4392
I think this exception was actually caused by an issue in a LINQ to Entities query I modified in the BLL. I overlooked this change initially, but once I got the "offending" BLL class names changed, I was confronted with this exception:
Unable to cast the type 'System.Collections.Generic.IEnumerable
1[[OrgName.ProjName.bllTableB, ProjNameBLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' to type 'System.Collections.Generic.List
1[[OrgName.ProjName.bllTableB, ProjNameBLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. LINQ to Entities only supports casting EDM primitive or enumeration types.
This runtime exception was thrown when I called .ToList()
from GetTableAByID(Int32)
in the following example:
Public Class TableA
Public Property TableAID as Integer
Public Property TableBs as New List(Of TableB)()
Public ReadOnly Property Amount as Decimal
Get
If Me.TableBs.Count > 0 Then
Return Me.TableBs.Sum(Function(tb) tb.Amount)
Else
Return 0
End If
End Get
End Property
Public Shared Function GetTableAs() As IQueryable(Of TableA)
Dim db As New DataAccess.ProjNameEntities()
Return (
From ta In db.TableAs
Order By ta.PaidDate Descending
Select New TableA With {
.TableAID = ta.TableAID,
.TableBs = (
From tb In db.TableBs
Where tb.TableAID = ta.TableAID
Select New TableB With {
.Amount = tb.Amount
}
)
}
)
End Function
Public Shared Function GetTableAByID(TableAID as Integer) as List(Of TableA)
Return (
From ta in GetTableAs()
Where ta.TableAID = TableAID
).ToList()
End Function
End Class
Public Class TableB
Public Property TableBID as Integer
Public Property TableAID as Integer
Public Property Amount as Decimal
End Class
Obviously there should be no problem converting from DbQuery<T>
to List<T>
, so I suspected the exception messages were just taking me on a wild goose chase. I decided to look at the query to see if something in there was causing the problem. Sure enough, that's where it was.
The offending section of this code seems to be where I select a New TableB
into the List(Of TableB)
. I refactored the class & query to avoid this pattern (all I really needed was the Sum of TableB.Amount) and all errors cleared up.
I would love if any light could be shed on this issue and the VERY MISLEADING exceptions that resulted.
Edit:
I have increased the facepalm factor by discovering that just adding .ToList()
to the end of the sub-query also fixes the issue:
.TableBs = (
From tb In db.TableBs
Where tb.TableAID = ta.TableAID
Select New TableB With {
.Amount = tb.Amount
}
).ToList()
Upvotes: 2