4

I've been scratching my head for hours with a peculiar issue in Entity Framework.

The following questions are not duplicates since their answers do not help me:

I have an aspnet core mvc app (v1.1) that references a .NET Framework 4.7 project (the Data Access Layer) with Entity Framework 6.1.3 installed as a NuGet package.

I'm using the designer so I have an .edmx file.
I'm getting the following exception thrown at my face in runtime:

System.Data.Entity.Core.MetadataException: Unable to load the specified metadata resource.
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoaderCompositeResource.LoadResources(String assemblyName, String resourceName, ICollection`1 uriRegistry, MetadataArtifactAssemblyResolver resolver)
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoaderCompositeResource.CreateResourceLoader(String path, ExtensionCheck extensionCheck, String validExtension, ICollection`1 uriRegistry, MetadataArtifactAssemblyResolver resolver)
   at System.Data.Entity.Core.Metadata.Edm.MetadataArtifactLoader.Create(String path, ExtensionCheck extensionCheck, String validExtension, ICollection`1 uriRegistry, MetadataArtifactAssemblyResolver resolver)
   at System.Data.Entity.Core.Metadata.Edm.MetadataCache.SplitPaths(String paths)
   at System.Data.Entity.Core.Common.Utils.Memoizer`2.<>c__DisplayClass2.<Evaluate>b__0()
   at System.Data.Entity.Core.Common.Utils.Memoizer`2.Result.GetValue()
   at System.Data.Entity.Core.Common.Utils.Memoizer`2.Evaluate(TArg arg)
   at System.Data.Entity.Core.Metadata.Edm.MetadataCache.GetArtifactLoader(DbConnectionOptions effectiveConnectionOptions)
   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.Internal.InternalConnection.CreateObjectContextFromConnectionModel()
   at System.Data.Entity.Internal.LazyInternalConnection.CreateObjectContextFromConnectionModel()
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   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.GetEnumerator()
   at System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)

And I have the following connectionstring:

metadata=res://*/MyContext.csdl|res://*/MyContext.ssdl|res://*/MyContext.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=MyDatabase;integrated security=True;multipleactiveresultsets=True;App=EntityFramework"

The thing is, when running in a Debug build, the application runs without any problems. However when running in a Release build, the exception gets thrown. But if I disable optimize code on the project with the .edmx file, the exception is not thrown anymore.

I've even looked in the Entity Framework source code. You can see from the Stacktrace, that this exception gets thrown at line 170, because loaders.Count == 0. I don't understand why the resources can't be loaded from the assembly in a Release build, while it works in a Debug build.

EDIT I just installed a trial version of Reflector to inspect the assembly. So when looking into the .dll file that has been built with the Debug configuration, I can clearly see the 3 resource files embedded. However, in the assembly that has been built with a Release configuration, oddly the resource files are missing!

QuantumHive
  • 5,613
  • 4
  • 33
  • 55
  • I'm encountering a very similar problem, and my setup is similar to yours - full framework entity framework project, referenced by .net core mvc app. If I run my project in VS, or deploy it to Azure, all is good. If I use "dotnet run" from the command line, I would occasionally get this error. Doing a clean and rebuild in VS has until now got rid of it. But now on just one of my 2 dev machines this happens every time using "dotnet run" but not at all in VS! Will continue digging, but please do update this is you find anything further. – fubaar Aug 18 '17 at 11:17
  • Just as a follow up. I have confirmed what you have observed - the resources are not being included in the Entities assembly when I build the project using dotnet run. Maybe it is the same underlying issue. Thanks for your post anyway - I have got around it by just running the .exe generated by VS rather than using dotnet run (which insists on rebuilding the Entities assembly). – fubaar Aug 18 '17 at 11:27
  • 1
    This seems to be a bug in the dotnet CLI: see [this issue](https://github.com/dotnet/cli/issues/6812). It's unfortunate that Microsoft does not respond to this. – QuantumHive Aug 19 '17 at 11:48
  • I am also having this problem. I will try to remember to answer here when i solve it. – catbadger Jul 10 '19 at 15:02

1 Answers1

2

I've found a workaround for this problem.

I'm targeting a .net core 3.1 linux container with a legacy project using an EDMX. Migration of the whole EDMX in code first will take a lot of time so I had to find a solution to make it work in Azure.

Problem is that a dotnet cli command like dotnet publish does not embed csdl, msl, or ssdl files see issues #8932. So do not embed it in the output assembly. This is partially explained in this thread.

  1. First change the "Metadata artifact processing" property in the "Conceptual Entity Model" of the EDMX from "Embeded in Output Assembly" to "Copy to output directory" this picture show you how.

Note that you need to open the EDMX file (double click), since a single click will show you the property of the file.

  1. copy the csdl, msl, and ssdl files for publishing. I do that with a PostBuild event in the csproj file. Like this :
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Condition="'$(OS)' == 'Unix'" Command="echo *** cp -a $(ProjectDir)$(OutDir)* $(TargetDir)" />
    <Exec Condition="'$(OS)' == 'Unix'" Command="cp -a $(ProjectDir)$(OutDir)* $(TargetDir)" />

    <Exec Condition="'$(OS)' != 'Unix'" Command="echo *** copy /Y $(ProjectDir)$(OutDir)* $(TargetDir)" />
    <Exec Condition="'$(OS)' != 'Unix'" Command="copy /Y $(ProjectDir)$(OutDir)* $(TargetDir)" />
</Target>

Note that you'll have to adapt the copy command paths to match your solution (for example if your EDMX project is located in a sub-project).

  1. Finally change your connection string to a non embeded metadata (see doc here).

From

connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string=[...]"

To

connectionString="metadata=/app;provider=System.Data.SqlClient;provider connection string=[...]"

Since the folder change upon release/debug/publish I've created this small function in my application to change the connection string :

private string EntityConnectionString(string connectionString)
{
    EntityConnectionStringBuilder csb = new EntityConnectionStringBuilder
    {
        ProviderConnectionString = connectionString,
        Metadata = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
    };
    csb.Provider = "System.Data.SqlClient";
    return csb.ConnectionString;
}

Just provide the connection string in the classical non EDMX format to the connectionString parameter of the function. ie :

data source=[...];initial catalog=EntityframeworkTest;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework
SuperPoney
  • 563
  • 6
  • 21