Reputation: 5683
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!
Upvotes: 4
Views: 1381
Reputation: 660
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.
Note that you need to open the EDMX file (double click), since a single click will show you the property of the file.
<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).
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
Upvotes: 2