Reputation: 1230
First, this is related to another question here on SO:
I've read and debugged my issue with the following SO article & blog:
MetadataException: Unable to load the specified metadata resource
and
http://blogs.teamb.com/craigstuntz/2010/08/13/38628/
BUT...I'm still having questions beyond just this 'fix'
I have a WebAPI (2.1), the connection string in my WebAPI is as so:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://*/ProjectModel.csdl|
res://*/ProjectModel.ssdl|
res://*/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
When I call ToList()
on a DbSet
in my WebAPI (pseudo code):
DbContext _DbContext = new ProjectEntities();
DbSet<TEntity> _dbSet = _DbContext.Set<TEntity>();
_dbSet.ToList();
It works great!
When I call the same from within a WINDOWS SERVICE, I get the following error:
The app.config entry for the connection string is exactly the same as the web.config:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://*/ProjectModel.csdl|
res://*/ProjectModel.ssdl|
res://*/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
Now, the blog shows to reference the dll manually as so:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://Project.Data.dll/ProjectModel.csdl|
res://Project.Data.dll/ProjectModel.ssdl|
res://Project.Data.dll/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.0.1;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
This does NOT work/fix the issue
The only way I've been able to fix it, is to use the fully qualified name:
<connectionStrings>
<add name="ProjectEntities" connectionString="
metadata=res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.csdl|
res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.ssdl|
res://Project.Data, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null/ProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=192.168.250.125\sqlexpress;
initial catalog=Project;
persist security info=True;
user id=***;
password=***;
multipleactiveresultsets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
Why does this work like this? Why would this work in a web project, but not a windows service project?? I recently changed from EF5 to EF6, and this error has popped up - all this code worked previous to upgrading EF. Does anyone have any insight as to why and how/if I can just use * for the dll name in my connection string?
I thought it was an issue of where the service .exe was running and a file wasn't copied locally, but nope, the Project.Data.dll is there and it's the right version.
I used FusionLog to try and find the error, and no luck there. I'm pretty confused.
Upvotes: 9
Views: 8562
Reputation: 7017
Why this happens?
The issue you are having is just a result of extra security measures to prevent binary planting or DLL hi-jacking attack (read more) when running your application as as windows service.
Why should I care?
As you probably know, there is a specific, well documented order in what every referenced DLL file is looked up. Usually it starts to search DLL in current application directory and then goes away to more "public" locations like PATH
folders, GAC, etc.
Main idea of binary planting is to plant malicious DLL file in a folder which is checked before folder of the legit DLL. Loading such malicious DLL would allow attacker to gain control over the system.
Usually windows services run under elevated account (LocalSystem, LocalService, NetworkService, etc) therefore windows services are good target for binary planting attacks.
What can I do?
Microsoft have taken extra precaution steps to reduce security risks and there is a good reason for that. But you can try to work around you issues.
1) Current directory is not what you expect
Windows service starts in system folder (usually something like C:\Windows\System32
)
Good news are that it is very easy to fix. You just have to change current directory on services startup.
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
See blog post from Phil Haack;
2) Read documentation thoroughtly
According to EF documentation, wildcard character has special meaning and it limits places where runtime will look for DLL files:
If you specify a wildcard (*) for assemblyFullName, the Entity Framework runtime will search for resources in the following locations, in this order:
1) The calling assembly.
2) The referenced assemblies.
3) The assemblies in the bin directory of an application.
As your working folder is set to system folder and you references probably are not there, EF might end up looking in wrong places and your assemblies containing resources might not be loaded.
3) Stay safe with fully qualified assembly names
Although I am not completely sure about this and haven't tested, but Microsoft just might have disallowed Windows services to load DLL without providing fully qualified assembly name to reduce risk of injecting malicious DLL files;
Good read on securing your Windows services here (specially chapter 5).
4) Debug it!
EF6 happens to be open source project. This means that you can get full source of it and debug it. You can find project on CodePlex here.
Upvotes: 5
Reputation: 141542
I'm afraid I wasn't able to reproduce the error that you received, or answer why you needed to change the metadata
.
That said, I did learn that, for the EF connection string, the Windows Service required a different provider connection string
than the WebApi did.
The following are the steps to reproduce your error. The only difference is that I'm using localdb not SQLExpress.
The resultant code from my steps-to-reproduce is online at GitHub here: https://github.com/bigfont/EntityFrameworkWindowsServiceWebApi.
Here are those steps:
Install-Package EntityFramework -ProjectName MyWebApi
ValuesController
with a Get
method that queries the database.See: https://msdn.microsoft.com/en-us/data/jj205424.aspx
Install-Package EntityFramework -ProjectName MyWindowsService
NT AUTHORITY\SYSTEM
as a localdb Login and as a MyService db User.PowerShell Installation, Startup, and Uninstall
Release> installutil .\MyWindowsService.exe
Release> Start-Service MyService
Release> installutil .\MyWindowsService.exe /u
In the connection string for the Windows Service, I wasn't able to use (localdb)\v11.0
. Instead, I needed to use the named pipe. I found the named pipe with this command line:
> SqlLocalDB.exe info v11.0
Name: v11.0
Version: 11.0.2100.60
Shared name:
Owner: MY_COMPUTER\Shaun.Luttin
Auto-create: Yes
State: Running
Last start time: 2015-04-09 5:54:34 PM
Instance pipe name: np:\\.\pipe\LOCALDB#1010101\tsql\query
The resultant connection string, using the Instance pipe name, looked like this.
<connectionStrings>
<add name="MyProjectModelContainer"
connectionString="
metadata=
res://*/MyProjectModel.csdl|
res://*/MyProjectModel.ssdl|
res://*/MyProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=np:\\.\pipe\LOCALDB#4BCE6D95\tsql\query;
initial catalog=MyService;
Integrated Security=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
Whereas the WebApi connection string looked like this:
<add name="MyProjectModelContainer"
connectionString="
metadata=
res://*/MyProjectModel.csdl|
res://*/MyProjectModel.ssdl|
res://*/MyProjectModel.msl;
provider=System.Data.SqlClient;
provider connection string="
data source=(localdb)\v11.0;
initial catalog=MyProject;
integrated security=True;
MultipleActiveResultSets=True;
App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
See also: http://www.connectionstrings.com/sql-server-2012/
Needing to use a different connection string with a Windows Service that we do with a WebApi project is a similar problem to what you found. From Sql Server Management Studio, from Visual Studio, and from the WebApi, we can connect by calling the data source (localdb)\v.11 whereas from a Web Service we need to call it by it's instance named pipe.
Here's a suspicion: It might be that there are multiple instance of localdb on the computer, and that we needed to absolutely specify which one we want to use. Unfortunately, this doesn't help answer why you needed to change the metadata
.
This is a similar though different problem than what you faced, because you needed to change the Entity Framework metadata
whereas I needed to change the provider connection string
. Coincidence?
Upvotes: 1
Reputation: 146
Please follow the steps bellow:
1.Write click on edmx file and then click open with of the related entity.
2.Select xml editor and click open.
3.Scroll from top to bottom of the .edmx xml file and look for any error marks.
4.If you mind errors then fix that. 5.Rebuild the solution and if no errors found then congratulations :)
Upvotes: -1
Reputation: 595
Copy the dll containing ProjectEntities to different path and then reference the same in your service project.
Upvotes: 2