This topic identifies common pitfalls in DevForce Code First development and what to do about them.
Few human endeavors proceed perfectly. Code First development has its share of traps and tribulations. This topic covers some of the more frequently encountered problems and annoyances.
A build-time error message that reads, in part,
EntityType 'EntityAspect' has no key defined. Define the key for this EntityType.
means you defined a custom DbContext but you neglected to ignore the DevForce EntityAspect type in your DbContext.
A runtime exception thrown on the server-side EntityServer is propagated back to the client's EntityManager. DevForce then re-throws it as an EntityServerException.
Sometimes the client-side environment swallows this exception; Windows Presentation Foundation (WPF) is notorious in this regard. It's always a good idea to attach an event handler to the EntityManager.EntityServerError and give yourself a place to set a breakpoint.
Here's an example from the "CodeFirstWalk" sample code:
C# | private void SetupManager() { Manager = new ProductEntities(); Manager.EntityServerError += (s, e) => Log("Server error:" + e.Exception.Message); } } |
You can set the breakpoint inside the lambda expression or inside the Log method.
When refactoring your model (e.g., breaking out a model project from your web project), you will relocate the ibmmx metadata file to the new model project. Make sure that the ibmmx file "Build Action" in the Property Window is set to "Embedded Resource".
If you neglect this step, you may encounter a parade of bizarre complaints (null reference exception, "can't call an internal ctor", etc.) when running in n-tier mode (e.g., in Silverlight). The root cause: although you see the ibmmx file in your project, DevForce can't find it at runtime.
A DevForce entity metadata file (a file ending in ".ibmmx") is supposed to re-generate when you change your entity model classes and rebuild the project.
Maybe it didn't. See if DevForce failed to generate metadata
If is always safe to delete a metadata file and re-build the project. The build process should regenerate it in the EntityModelMetadataDeploy task. If you do not see a new metadata file in your project, you know that DevForce was unable to create the metadata for some reason.
Open the Visual Studio Output Window and show the output from the "Build" process.
The task writes information, warning and error messages to the Build output pane. If you see a warning or error message, it will usually be accompanied by an explanation of the failure and what to try next.
If you don't see the messages from the EntityModelMetadataDeploy task, then it may not be injected into the MSBuild pipeline. Check that the .cf marker file is present. Also see the Generate Metadata topic.
Also check the version number shown for the IdeaBlade.VisualStudio.Build.Tasks assembly. This is output by the task when it starts. The version number of this assembly should match the DevForce version you are referencing in your project.
Although neither you nor any other member of the team has made a change to the model, the metadata are regenerated when you build the application. You notice this because your source control system tells you that the ibmmx file has pending changes.
The probably cause is that your DevForce license key is not the same one that was used when the metadata were last generated and checked in. In the current DevForce release, a different license key is one reason to rebuild the metadata.
There are a couple of workarounds.
For a future release, we are considering an option that would prevent regeneration of metadata when the only difference is a license key change.
That begs the question of how to force regeneration if you actually want to regenerate. That's easy: delete the ibmmx file and rebuild; it will be regenerated as part of the build process.
You pressed [F5-Start Debugging] shortly after making a change to the model or perhaps after deleting the metadata file to force it's regeneration. The application compiled without error and started. But it crashed as soon as it made the first query. The exception, if you were lucky enough to catch it, was "No datasource key found for this entity type: ...".
Yet if you stop debugging and press [F5] again, it compiles, runs, ... and works. What's going on?
What's going on is: you must build twice whenever the metadata file changes!
As explained in the metadata generation topic, DevForce generates the metadata file during the first build, after the initial compilation. It is too late to include the file in the model assembly as an embedded resource. The metadata file will be included in the assembly during the second build.
The evidence is in the Output window when you show the Build output. The first [F5] yields compilation #1 whose last output message reads: "Model metadata file '...' written for SomeFilename.ibmmx". The second [F5] produces compilation #2 whose last output message reads: "Model metadata for SomeFilename.ibmmx is unchanged".
The "...ibmmx is unchanged" message is what you're looking for. So remember to build the project first .... and then press [F5].
The underscore ("_") character has a special meaning in DevForce; it is used as the distinguishing character in a "data source key extension". You cannot put an underscore in your DataSourceKeyName.
You might have two metadata ".ibmmx" files with conflicting model definitions.
The most likely cause: you added a new DbContext or renamed an existing DbContext.
The name of the metadata ".ibmmx" file corresponds to the DataSourceKeyName of your model. Changing the name results in generation of a newly-named metadata file, and removal of the old file (as of v.6.1.4). If the old file was not removed, when DevForce discovers two conflicting metadata files at runtime it throws an exception.
Solution: check for an obsolete metadata ".ibmmx" file and delete it. You can always delete all metadata ".ibmmx" files and re-build the project to generate the one true metadata file.
You may struggle to produce the correct mapping between entities and database objects. The error messages at build or run time are usually helpful in finding the problem but don't always provide sufficient guidance on how to do what you intended to do.
It's always good to have an idea about what has changed recently. Did you or someone else modify the model? Take note of the classes and associations that changed. Did you or someone else modify the design database? Take note of the tables, columns, and foreign key relationships that changed.
Then you might ask DevForce code generation for a little help. In a clean solution, try building a model using DevForce "Code Second". You can build the entire model or just focus on the suspect classes and associations. Compare the entity classes that DevForce generates with the entity class you wrote. You may find helpful clues in the way DevForce defined its mappings.
Are you sure that the design database and the production database have exactly the same schema? Customers frequently tell us that they are "sure" ... only to discover that someone else has made a change they didn't know about. Have you confirmed that the two schemas are the same using automated tools? 3rd party vendor, redgate offers highly regarded products that are well worth their price.
Check also that your application model assemblies on the server are the same assemblies you are using in development. Again, customers frequently tell us that they are "sure" ... only to discover that someone else has deployed different assemblies or that the proper assemblies were not deployed. Make sure you update your application version numbers every time you build. A good Continuous Integration practice can help.
You'll get the following build error if you neglected to reference the PostSharp.dll (or PostSharp.SL.dll in Silverlight) in your model project(s):
Cannot resolve dependency to assembly 'PostSharp, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7' because it has not been preloaded
Also make sure you deploy the PostSharp.dll with your application.
In a WPF application project which references your model project you may see a compiler warning from Microsoft.WinFX.targets stating: "Unknown build error, 'Cannot resolve dependency to assembly 'PostSharp'...'".
True, your Code First model is not in the application project. Unfortunately, WPF won't follow dependent assemblies during XAML compilation and therefore complains. Add the PostSharp assembly reference and this problem goes away.
But then you get a PostSharp warning about performance because PostSharp realizes it has nothing to process. You can disable PostSharp for this project.
The PostSharp library isn't required to run a Silverlight application. But it is required if you use Expression Blend to design Silverlight views. Blend uses WPF XAML compilation under the hood and has the same problem probing referenced assemblies that we described above. The resolution is the same as well: add a reference to PostSharp.SL.dll and then disable PostSharp processing of this project if (as expected) you aren't using AOP with any of this project's classes.
Note also that Blend works only with the "Debug" configuration. If you build the application in Visual Studio with a different configuration, you may have to re-build it in the "Debug" configuration. You can do that in Blend itself from the "Project" menu.
While re-building you get the following Build error:
Among the likely causes: you deleted solution binaries in order to clear out lingering out-of-date assemblies. Unfortunately, the PostSharp process becomes confused and prevents you from building the model assembly.
Workaround: Close and re-open the solution.
You will get this error when installing the PostSharp package to a project which does not need to be enhanced with PostSharp aspects. You can remove the file named RequiresPostSharp.cs (or .vb) from the project to clear the error.
You will also get this error after using NuGet Package Restore to restore the DevForce 2010 Code First package, which also installs PostSharp. The fix is to do as the error message states, and build the solution one more time.
The EntityModelMetadataDeploy task should run only for .NET projects. You will receive this error if the Silverlight project contains a .cf marker file, which triggers this task. To resolve the problem, either delete the marker file from the Silverlight project, or edit the project file to remove the import of the DevForce build targets file.
Is the compiler complaining about a class derived from DbContext? DbContext is an Entity Framework component that doesn't exist in Silverlight. It shouldn't be in your Silverlight model project either; delete the file or file link from your Silverlight project.
Did the compiler complain that it cannot find assemblies for attributes. Are you still using DevForce version 6.1.3 or earlier? Please upgrade if you can. But if you can't, you are probably missing the CodeFirstShim. The Entity Framework mapping attributes in the System.ComponentModel.DataAnnotations namespace types (e.g., Table) are not defined in Silverlight; they make sense on the server but not on the client.
For applications written with a version of DevForce prior to 6.1.4, we ask you to include the following "shim" code at the top of every model class file:
C# | #if SILVERLIGHT using IdeaBlade.EntityModel.CodeFirstShim; // empty Silverlight definitions for EF attributes #endif |
This expedient is no longer necessary as of DevForce version 6.1.4; these empty attribute definitions are baked into the DevForce Silverlight IdeaBlade.EntityModel library and namespace.
A build-time error that reads:
Unable to find the requested .Net Framework Data Provider. It may not be installed.
indicates that the providerName on the connectionString is either invalid or is the EntityFramework EDM provider "System.Data.EntityClient" used with "Database First" or "Model First". You were probably migrating from a Database First model to a Code First model and overlooked this step while converting the EDM connection string to a normal database connection string.
You can remove the providerName attribute to allow it to default to the default value, “System.Data.SqlClient” or you can set it to a valid database provider name. Remember that this connectionString is in the app.config of the project you are building.
You know that you are assigning a property but the value isn't showing up in the database. Check the access modifiers for your property's get and set methods. Are they public? If not, you must take extra configuration steps so that Entity Framework knows that you intend to map that control and to ensure that DevForce can serialize non-public data.
You'll get this runtime exception if the Silverlight project holding your model does not contain an InternalsVisibleTo attribute to make entity "internals" visible to the System.Runtime.Serialization assembly.
If you don't specify a connection string that Entity Framework can find, it may try to create a database that matches your entity model. It will attempt to create that database on SQL Server Express. You'll receive an error if SQL Express is not installed. You can change this default.
At build time, DevForce will construct a new DbContext in order to obtain model metadata; at runtime, DevForce will construct a new DbContext for every query or save. This means the constructor on your custom DbContext class will be called at build time, and at run time for every query and save. For these reasons, you don't want to put logic here which should not be executed unintentionally or more than once. For example, instead of using a Database.CreateIfNotExists() call, which will connect to the database server to check for the existence of the database every time it's called, instead use a database initialization strategy, for example Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyDbContext>()). Better yet, place the initialization strategy in the static initializer for your custom DbContext.
In assemblies referencing your Code First model you may see a compiler warning from PostSharp like the following: "The module 'MyLib.dll' does not contain any aspect or other transformation.
For improved build-time performance, consider disabling PostSharp for this module by setting the compilation symbol (aka constant) 'SkipPostSharp' in your project, or set the MSBuild property 'SkipPostSharp=True'." The easiest way to resolve this is to go to the Properties page for your project and on the PostSharp tab set "Disable PostSharp" to "yes". Note that this properties tab is not available with the PostSharp NuGet install.
A WPF application project which references your model project may produce a compiler warning from Microsoft.WinFX.targets stating: "Unknown build error, 'Cannot resolve dependency to assembly 'PostSharp'...'". See discussion and resolution above.
If you're using Entity Framework 4.4 and you've used "Code Second" to generate your model, you'll see this build error or ones like it for the ColumnAttribute, DatabaseGeneratedAttribute, and others. EF 4.4 uses a different namespace for these attributes so you must adjust the imported namespace as shown here.
You may get this error from the EntityModelMetadataDeploy build task when you first try to build a Code First model after upgrading DevForce. After upgrading your DevForce version, be sure to also upgrade to the corresponding version of the DevForce Code First NuGet package.
You'll get this error in a Silverlight application when doing an ImportEntities call, using faking, or working with an EntityCacheState if you haven't granted IdeaBlade.Core.SL "friend" access to internals within your assembly. To fix the problem, add the following in the AssemblyInfo file in your model projects:
C# | [assembly: InternalsVisibleTo("IdeaBlade.Core.SL, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b3f302890eb5281a7ab39b936ad9e0eded7c4a41abb440bead71ff5a31d51e865606b2a7e6d0b9dd0d92b113b9d10fb13f01fb5d856e99c1e61777cf4772d29bad7e66ffb93fc5cbd63b395046c06ff57db6ecbeee4bdd6effc405878d65cfc4911708ed650da935d733fc5dc707f74910e025ac080543e01a6cc863b9f85ffc")] |
DevForce automatically does a Refresh call after saving database changes. Unfortunately when using Code First with a SQL 2005 database this causes escalation of the TransactionScope to use a distributed transaction.
You can turn off DevForce's use of the TransactionScope by setting the TransactionSettings.Default static property. Here's an example - note that it's the false argument to the constructor which disables TransactionScope, the other arguments determine the transaction isolation level and timeout, with defaults shown here:
C# | TransactionSettings.Default = new TransactionSettings(System.Transactions.IsolationLevel.ReadCommitted, TimeSpan.FromSeconds(30), false); |
Note that your saves will still be transactional, as a database transaction is still used. The change ensures that the database transaction will not be enlisted in a TransactionScope.
DevForce currently uses PostSharp version 2. If you manually install or upgrade the PostSharp NuGet package, be sure to specify the version number so that you don't mistakenly upgrade to version 3. More information is available here.
You can post a message on the DevForce forum or contact Technical Support.
Before you do, you might try the following: