Feed Subscribe
Exception has been thrown by the target of an invocation.


Entity Framework metadata deployment

by ondrejsv 15. March 2008 18:41

Every project targeting Microsoft ADO.NET Entity Framework requires creating a conceptual entity data model, a storage model describing physical data source and a mapping between these two. Each of these metadata resides in its own XML file with its own XML schema (CSDL, SSDL and MSL, respectively). Today I want to spend a few words about what possibilities you have regarding deployment of them.

Besides very simple scenarios I suppose that all Entity Framework models will be defined within standalone projects that are part of a bigger solution to preserve modularity and maintainability. Such a project contains one or more .edmx files created and edited in the Visual Studio EDM designer most of the time. An .edmx file is just a blend of CSDL, SSDL, and MSL content together with some data for diagramming support. This file is processed by EntityModelCodeGenerator tool which extracts corresponding .csdl, .ssdl and .msl files. Note that hand crafting these files is a perfectly valid way.

You then write path to the metadata files (.csdl, .ssdl and .msl) as part of your Entity Framework client connection string:


<connectionStrings>
<add name="NorthwindEntities"
connectionString="metadata=.\Northwind.csdl|.\Northwind.ssdl|.\Northwind.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SERVER\SQL2000;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=False&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

You have a couple of choices here (most of them have not been fully documented yet). An obvious choice is a physical file system path. The problem with this approach is that if you write a relative path, it is resolved against the current directory which is a very fragile thing. Take for example an ASP.NET web site/project -- the current directory is set up by the process hosting the site and it's usually anything what you don't expect. When you host your site in the IIS, most likely it's the system directory, while when you are debugging the site inside Visual Studio ASP.NET Development Server, it's C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE or something similar.

Better alternative in case of an ASP.NET web site is to use a well known tilde (~) prefix. This option is undocumented as far as I know. Internally the Entity Framework resolves the path using the HostingEnvironment.MapPath method so you may use any virtual path. For example let's suppose that we have a directory called models in the root virtual directory. Then the connection string could look like:


<connectionStrings>
<add name="NorthwindEntities"
connectionString="metadata=~\models\Northwind.csdl|~\models\Northwind.ssdl|~\models\Northwind.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SERVER\SQL2000;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=False&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

The last option is to embed the metadata as resources. Let's say that our Entity Framework models (.edmx files) are defined within a project called Models. We can automatically embed the generated .csdl, .ssdl and .msl files as resources by setting the Metadata Artifact Processing property of the conceptual entity model (this is what shows up when you open an .esmx file in the Visual Studio) to Embed in Output Assembly:

EmbedMetadata

However, do not set Build Action in the properties of the .edmx file itself to Embeded Resource nor to Resource, just leave it as Entity Deploy:

EdmxFileProperties

Of course, if you craft the files by hand, just embed them into the assembly as usual (Build Action to Embeded Resource for all of them).

Entity Framework client connection string recognizes the res:// prefix for locating metadata in resource manifests. Common syntax is res://assembly/resourcepath. In our case it could be:


<connectionStrings>
<add name="NorthwindEntities"
connectionString="metadata=res://Model/Northwind.csdl|res://Model/Northwind.ssdl|res://Model/Northwind.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SERVER\SQL2000;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=False&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

One special note: instead of a specific assembly name you may use an asterisk (*) to force the Entity Framework to search in all referenced assemblies which is quite useful when you have a single ASP.NET web site (not an ASP.NET web project) with .edmx files placed directly in the site -- then the ASP.NET compiler generates many assemblies with generated names so you don't know the name of the assembly your metadata will be part of in advance:


<connectionStrings>
<add name="NorthwindEntities"
connectionString="metadata=res://*/Northwind.csdl|res://*/Northwind.ssdl|res://*/Northwind.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=SERVER\SQL2000;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=False&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

Unfortunately, the tilde (~) and res:// prefix for locating files given as either virtual paths or resources are not any kind of widely universal standard in the .NET framework. Entity Framework parses input and locates files on its own as many other frameworks/libraries and even parts of the .NET framework itself. I think that a standard describing common schemas (prefixes) and a standard component in the .NET framework handling it would be very useful.

kick it on DotNetKicks.com

Tags: ,

AppSettingsReader and reading typed and nullable application settings

by ondrejsv 8. January 2008 21:38

I'm pretty sure that almost every .NET developer has heard of the possibility to include various application settings in the configuration file (app.config or web.config). Application settings are just plain string key-value pairs we used to store in the past inside plethora of .ini files and in the Registry. A common way to read them in the .NET framework is to use the static AppSettings property of the ConfigurationManager class. This property returns an object of the NameValueCollection type containing the application settings key-value pairs.

However, there is a class named AppSettingsReader which is a wrapper around the AppSettings collection and I found that only a few know it. The only "useful" method (i.e. not inherited from the Object class) is the GetValue method. It takes two parameters: key of the application settings item we want to retrieve and type to convert the value to, so we don't need to convert the value manually. Another difference over directly using AppSettings is that this method throws InvalidOperationException if no item with the specified key is present.

Suppose we have a configuration file as following:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="Color" value="Red"/> <add key="FailAttemptCount" value="5" /> </appSettings> </configuration>

We may then use the following snippet to read these settings:

AppSettingsReader appSettingsReader = new AppSettingsReader(); Console.WriteLine( appSettingsReader.GetValue("Color", typeof(string))); int failCount = (int) appSettingsReader.GetValue("FailAttemptCount", typeof(int));

Internally GetValue uses the ChangeType method of the Convert class to explicitly convert the read value. However, GetValue returns the value as object so we must use a formal downcast to assign the value to a typed variable (see below for a possible solution). Note that the Convert class supports converting only to .NET base types.

There is another feature of the AppSettingsReader which is undocumented as far as I know. For string values you may use "(None)" as value to indicate there is in fact no value but the setting is present. In a rare case you want to have "(None)" as a valid value, just enclose it within parenthesis. If we introduce these values to our configuration file:

<add key="PreferredName" value="(None)"/> <add key="NoneString" value="((None))"/>

then

appSettingsReader.GetValue("PreferredName", typeof(string))

returns a null string and

appSettingsReader.GetValue("NoneString", typeof(string))

returns "(None)" as value.

As I've mentioned, in the original implementation this trick works only for string values but it is straightforward to extend it to other types. In the attached demonstration project you can find MyAppSettingsReader class -- an implementation derived from AppSettingsReader. Then if we have:

<add key="PreferredName" value="(None)"/>

then

int? maxOpenFiles = (int?)myAppSettingsReader.GetValue("MaxOpenFiles", typeof(int?));

maxOpenFiles will be assigned a null value.

Last note: the final downcast is very annoying and indeed it could be made unnecessary by implementing GetValue method as a generic one. With introduction of extension methods, we can atone this very easily with no access to original code and even without creating a derived class:

public static T GetValue<T>(this AppSettingsReader appSetReader,string key) { return (T)appSetReader.GetValue(key, typeof(T)); }

Then we may use it just like:

Console.WriteLine( appSettingsReader.GetValue<string>("Color")); failCount = appSettingsReader.GetValue<int>("FailAttemptCount");

I attached a demonstration project so be sure to have a look at it.

kick it on DotNetKicks.com

Tags:

"String cannot have zero length" when you try to install a .vsi file

by ondrejsv 21. December 2007 14:28

Today I decided to have a look at the recently released Visual Studio 2008 and .NET Framework 3.5 Training Kit which contains several presentations, labs and samples. I opened the first demo (about LINQ but it's not important right now), read the supplied instructions and run setup.bat. This small batch file does some nifty work, including running LINQ.vsi full of code snippets. But I ended here because a message box reading "String cannot have zero length" popped out at me. Fortunately I wasn't the first one having this issue and I found a forum topic on the asp.net site where Frank Tse (member of the ASP.NET team) explains that some missing Registry keys may be cause of this problem:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\ContentTypes\VSTemplate]
"ClassName"="VSContentInstaller.VSTemplateInstallerPage"
"DisplayName"="#VSTemplateContentDisplayName"

Although my Registry missed them too, unfortunately adding them didn't help me. As the .vsi file I tried to install contains just code snippets, I checked also the Code Snippet key in the same subtree and indeed, there were no ClassName and DisplayName values so I asked one of my colleagues to look for them in his computer. They are:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSEnvCommunityContent\ContentTypes\Code Snippet]
"ClassName"="VSContentInstaller.SnippetInstallerPage"
"DisplayName"="#CodeSnippetsContentDisplayName"

After inserting them I completed the installation successfully. :-)

Tags:

My fight with Windows Live Writer Installer

by ondrejsv 19. December 2007 13:46

First time I tried Windows Live Writer a few months ago when I was experimenting with BlogEngine.NET. I found the tool to be quite useful for writing blog posts offline so when I finally created my own blog I immediately downloaded the installer in order to start writing on my notebook. Everything seemed to be fine until the progress bar captioned "checking your computer for Windows Live programs" changed to "Sorry, this is taking a little longer than expected". I was really patient but as I don't see a half an hour as little longer I killed the installer and began investigating.

wlinstall

During the installation I was looking what processes were active and so I found out that Windows Live Writer Installer actually uses Windows Update to download files and install them. Windows Update logs its activity to WindowsUpdate.log file which you can find in your Windows base directory. It may be fairly big in size so try to open it in something smarter than Notepad (like Ultraedit or Notepad++ in which it does not take forever). Surely, it was full of errors:

WARNING: WinHttp: ShouldFileBeDownloaded failed with 0x80072efd

WARNING: SyncServerUpdatesInternal failed: 0x8024402f

Obviously WinHttp component couldn't download any files. The error codes represent general errors like connection timeout and there was an error while downloading files and looking for them gave me at first only general advices as well (check you are connected to internet and so on). I tried to shut down the firewall and even my antivirus software and rerun the installation but no result. As I use a proxy server to connect to the outside world I suspected Windows Update service for a while that it does not use it. A little more searching and I found a blog post describing one useful utility -- proxycfg.exe -- used to manage WinHTTP proxy settings which, as I learnt, are completely independent from IE settings (UPDATE 2007-12-27: See also the update section below how to configure proxy without proxycfg in Windows Vista). Indeed (and fortunately) when I run it, it showed me that WinHTTP had no proxy information and was trying to connect to internet directly:

proxycfg

By running proxycfg -u the utility imported my proxy settings from Internet Explorer:

proxycfg2

After restart I was happy to complete the Windows Live Writer Installer:

Untitled

Note: If you are curious, I found the rules Windows Update service uses to decide which proxy server to use.

Note 2: I couldn't find proxycfg utility on my Vista notebook, so I copied it from a Windows Server 2003 machine (look for it in the windows\system32 directory).

Note 3: In the past whenever Microsoft used an online installer, it used to offer also a completely offline installation package as well. If it was true in this case, I could possibly save almost two hours of investigating and trying to install Windows Live Writer.

UPDATE (2007-12-27): I learnt today that proxycfg utility has been displaced in Windows Vista and we should use the netsh utility to configure any proxy settings. Here is how the above mentioned procedure would look when done in netsh:

Run cmd.exe under administrator privileges and type netsh and then winhttp to enter WinHTTP context:

image

By issuing show proxy command you would see your current proxy settings (direct connection, indeed) and by import proxy ie you import settings from IE:

image

You may find more information in this Windows Core Networking blog post.

Tags:

My post zero — about me

by ondrejsv 17. December 2007 18:06

Hello and welcome to my blog. First let me introduce myself — my name is Ondrej Svačina and currently I live and work in Bratislava, the capital of Slovakia, a small country located in the heart of Europe, member of the EU. My official job title reads software architect and I work predominantly with .NET technologies at present.

Nowadays everybody around is constantly blogging about almost everything. As I perceive it "blog or die" or "who doesn't have a blog does not really exist", I finally managed to setup and create a blog of my own. December is a good month to do such things so I can say that I've had a blog since 2007 ;-) More importantly I sometimes have a feeling that I have something to tell the outside world which may be of interest to others. So stay tuned as I am and enjoy reading...

Tags: