zzzprojects / EntityFramework-Classic

Entity Framework Classic is a supported version of the latest EF6 codebase. It supports .NET Framework and .NET Core and overcomes some EF limitations by adding tons of must-haves built-in features.
https://entityframework-classic.net
Other
103 stars 27 forks source link

Unable to load the specified metadata resource. #70

Closed Mek7 closed 2 months ago

Mek7 commented 2 months ago

I am in the process of migrating a rather large solution from .NET Framework 4.8 to .NET 8. The bridge between these two is .net standard 2.0 that I want to use as a target of all class library projects. But the original EF from Microsoft targets only .net standard 2.1, so I chose EF Classic that targets .net standard 2.0. I deinstalled original EF and replaced with EF Classic nuget in all my solution projects. I thought it should be a drop-in replacement as it is just a fork with enhanced targeting and functionality. But as soon as my application tries to do a .Select() on a IQueryable, I get the following exception:

System.Data.Entity.Core.MetadataException
  HResult=0x80131939
  Message=Unable to load the specified metadata resource.
  Source=Z.EntityFramework.Classic
  StackTrace:
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoaderResource.LoadResource()
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoaderResource.CreateReader()
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoaderResource.CreateReaders(DataSpace spaceToGet)
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoaderComposite.CreateReaders(DataSpace spaceToGet)
   at System.Data.Entity.Core.Metadata.Edm.MetadataCache.LoadEdmItemCollection(MetadataArtifactLoader loader)
   at System.Data.Entity.Core.Metadata.Edm.MetadataCache.<>c__DisplayClass9_0.<GetMetadataWorkspace>b__0(String k)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at System.Data.Entity.Core.Metadata.Edm.MetadataCache.GetMetadataWorkspace(String cacheKey, MetadataArtifactLoader artifactLoader)
   at System.Data.Entity.Core.Metadata.Edm.MetadataCache.GetMetadataWorkspace(DbConnectionOptions effectiveConnectionOptions)
   at System.Data.Entity.Core.EntityClient.EntityConnection.GetMetadataWorkspace()
   at System.Data.Entity.Core.Objects.ObjectContext.RetrieveMetadataWorkspaceFromConnection()
   at System.Data.Entity.Core.Objects.ObjectContext..ctor(EntityConnection connection, Boolean isConnectionConstructor, ObjectQueryExecutionPlanFactory objectQueryExecutionPlanFactory, Translator translator, ColumnMapFactory columnMapFactory)
   at System.Data.Entity.Core.Objects.ObjectContext..ctor(EntityConnection connection)
   at System.Data.Entity.Internal.InternalConnection.CreateObjectContextFromConnectionModel()
   at System.Data.Entity.Internal.LazyInternalConnection.CreateObjectContextFromConnectionModel()
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.InternalContext.Initialize()
   at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
   at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
   at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
   at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
   at System.Linq.Queryable.Select[TSource,TResult](IQueryable`1 source, Expression`1 selector)
   at ...redacted....ToSystemUsers(IQueryable`1 systemUsers) in ...redacted...\ProjectionExtensions.cs:line 189

I have a file called SystemDatabase.edmx in a class library project that targets .net 8.0 so this should be the project where the metadata is taken from. The executing code where the .Select() fails, is in the same class library. It is called from a different project that represents an ASP.NET Core web project. I already cleaned and rebuilt solution. The EDMX file is present in the csproj file like this:

  <ItemGroup>
    <EntityDeploy Include="SystemDatabase.edmx" />
  </ItemGroup>

But Visual Studio 2022, when Properties of this file in Solution Explorer are opened, displays Build Action = None and does not let me enter EntityDeploy instead (property value is invalid)....? Maybe a bug in the SDK project system in VS. However, this setup has worked using the original MS EntityFramework. What can I do?

JonathanMagnan commented 2 months ago

Hello @Mek7 ,

Do you think you could create a runnable project with the issue? It doesn’t need to be your project, just a new solution with the minimum code to reproduce the issue.

FYI: I usually recommend you to keep using the official EF6 version and target multi framework if you don't need to use any new features introduced in EF Classic: http://dontcodetired.com/blog/post/Support-Multiple-Versions-of-NET-From-A-Single-Class-Library-(NET-multi-targeting)

Best Regards,

Jon

Mek7 commented 2 months ago

Hello @JonathanMagnan I created a minimal reproduction sample. It contains an ASP.NET Core web project and a .net standard 2.0 library. The library references EF Classic and has EDMX model inside it. It seems like the EDMX is not parsed into its CSDL, SSDL, MSL parts and these are not embedded in the .netstandard 2.0 library (hence EntityDeploy build action not being available on edmx file in VS). I am attaching a zip file of the sample solution. Hopefully we can find a way out :) Thanks in advance. ZEntityFramework.Web.zip

JonathanMagnan commented 2 months ago

Hello @Mek7 ,

I have made "maybe" some progression but didn't success to make it work.

I was looking at the following issue on Stack Overflow: https://stackoverflow.com/questions/45674708/unable-to-load-the-specified-metadata-resource-release-vs-debug-build

I successfully made the change in the edmx file:

  <Designer xmlns="http://schemas.microsoft.com/ado/2009/11/edmx">
    <Connection>
      <DesignerInfoPropertySet>
        <DesignerProperty Name="MetadataArtifactProcessing" Value="Copy to output directory" />
      </DesignerInfoPropertySet>
    </Connection>

Added the PostBuild (not sure if that is the right place):

    <Target Name="PostBuild" AfterTargets="PostBuildEvent">
        <Exec Condition="'$(OS)' != 'Unix'" Command="echo *** copy /Y $(ProjectDir)$(OutDir)* ..\ZEntityFramework.Web\" />
        <Exec Condition="'$(OS)' != 'Unix'" Command="copy /Y $(ProjectDir)$(OutDir)* ..\ZEntityFramework.Web\" />
    </Target>

or???

    <Exec Condition="'$(OS)' != 'Unix'" Command="echo *** copy /Y ..\ZEntityFramework.Library\bin\Debug\netstandard2.0\* $(TargetDir)" />
    <Exec Condition="'$(OS)' != 'Unix'" Command="copy /Y ..\ZEntityFramework.Library\bin\Debug\netstandard2.0\* $(TargetDir)" />

And changed the connection string:

var db = new ZEntityFramework.Library.SystemDatabase.MySystemDatabase("metadata=../ZEntityFramework.Library;provider=System.Data.SqlClient;provider connection string=\"Data Source=.\\sql2019;Initial Catalog=MyDatabase;Persist Security Info=True;User ID=MyUser;Password=MyPassword;Connect Timeout=10;multipleactiveresultsets=True;App=EntityFramework;Max Pool Size=500\"");      

While the file seems to be successfully found, there is still an issue with finding the model...

Unfortunately, I'm currently making no more progress on my side and I'm probably missing something to solve it.

If you want, you can try on your side, you might have more chance then me finding how to setup it correctly

Best Regards,

Jon

Mek7 commented 2 months ago

Hi, I probably understand what the problem is, found some other github issues but no fix, only some workarounds like you suggested. For now, I decided to go the multi-targeting route for another assembly that is a dependency of the Library project (it's a bit different situation than in the minimal repro solution) while still using the original EF from MS. What's interesting is that in my main solution with MS EF, the edmx file has correct Build action = EntityDeploy, with Custom Tool = EntityModelCodeGenerator and Custom Tool Namespace also set correctly. But as soon as I replace all MS EF nugets in the solution with Z EF Classic nuget, this EntityDeploy build action is no longer available.

JonathanMagnan commented 2 months ago

Hello @Mek7 ,

Awesome, I'm happy everything works with the original EF6 version. Staying with the original version and using EFE for Bulk Operations if you need it is probably the best way and safest way to go.

I will close this issue as I don't believe we will try to fix it

Best Regards,

Jon