Entity Framework RTM Breaking Changes

There were a number of changes to the Entity Framework and the designer between Visual Studio 2008 SP1/ .NET Framework 3.5 Beta and RTM that will require updates to existing source code. These breaking changes are listed below, including the mitigation for adjusting to the new behavior, and a side by side comparison of Beta code and RTM code.

Jeff Derstadt
Entity Framework Developer

Provider Changes

1. The Function property of DbFunctionCommandTree is renamed to EdmFunction.
Affects:
       - Provider writers

Mitigation:
       - Replace ".Function" wtih ".EdmFunction"

SP1 Beta Code:

 funcTree.Function

SP1 RTM Code:

 funcTree.EdmFunction
 

2. DbExpression, DbAggregate and DbModificationClause no longer have 'CommandTree' property

Affects:
       - Provider writers

SP1 Beta Code:  
Provider writers were able to go directly from an expression to a command tree, so when visiting          a command tree in CreateDbCommandDefinition, the expression instance was enough to have.

 public DbCommandDefinition CreateDbCommandDefinition(
DbProviderManifest manifest, 
DbCommandTree commandTree)
{
    commandTree.Validate();
    DbQueryCommandTree queryTree = (DbQueryCommandTree)commandTree;
    string tsql = this.Visit(queryTree.Query);
    return new CustomCommandDefinition(queryTree.Parameters, tsql);
}
 
...
 
public override string VisitParameterReferenceExpression(
DbParameterReferenceExpression expression)
{
    if (!expression.CommandTree.Parameters.ToDictionary(p => p.Key, p => p.Value).ContainsKey(expression.ParameterName))
    {
       throw new ArgumentException("Invalid parameter name");
    }
 
    return "@" + expression.ParameterName;
}

SP1 RTM Code:  
Providers must now keep a reference to the command tree that was passed as an argument to CreateDbCommandDefinition if they need to refer back to it.

 private DbCommandTree _currentTree;

public DbCommandDefinition CreateDbCommandDefinition(
DbProviderManifest manifest, 
DbCommandTree commandTree)
{
// no need to call commandTree.Validate()  
 // EF has already done so and the tree cannot change
DbQueryCommandTree queryTree = (DbQueryCommandTree)commandTree;
this._currentTree = commandTree;
string tsql = this.Visit(queryTree.Query);
return new CustomCommandDefinition(queryTree.Parameters, tsql);
}

...

public override string VisitParameterReferenceExpression(
DbParameterReferenceExpression expression)
{
if (!this._currentTree.Parameters.ToDictionary(p => p.Key, p => p.Value).ContainsKey(expression.ParameterName))
{
throw new ArgumentException("Invalid parameter name");
}

return "@" + expression.ParameterName;
}

3. DbCommandTree.Validate method is removed

Affects:
-
Provider writers

4. DbCommandTree and DbExpression no longer have a Clone method

Affects:
-
Provider writers

SP1 Beta Code:  
Provider writers were previously able to clone a command tree or a expression, e.g:

 DbCommandTree tree = ...
DbCommandTree clonedTree = tree.Clone();

DbQueryCommandTree queryTree = ...
DbExpression clonedExpression = queryTree.Query.Clone();
  

SP1 RTM Code:  
Providers will no longer be able to clone a DbCommandTree or a DbExpression

5. DbNaryExpression is removed from the CommandTree API

Affects:
       - Provider Writers

Workaround:
       - Use DbArithmeticExpression

SP1 Beta Code:

 IList<DbExpression> arg = ((DbNaryExpression)exp).Arguments;

SP1 RTM Code:

 IList<DbExpression> arg = ((DbArithmeticExpression)exp).Arguments;

6. ProviderManifest.Provider and ProviderManifest.Token properties have been removed

Affects:
       - Provider Writers

SP1 Beta Code:

 protected override DbCommandDefinition CreateDbCommandDefinition(
DbProviderManifest providerManifest,
DbCommandTree commandTree)
{     
  if(!SqlProviderManifest.ProviderInvariantName.Equals(
     providerManifest.Provider, StringComparison.Ordinal))
  {
   throw EntityUtil.Argument(...);
  }
 
  ...
 
}

SP1 RTM Code:

 protected override DbCommandDefinition CreateDbCommandDefinition(
DbProviderManifest providerManifest,
DbCommandTree commandTree)
{     
  if(!(providerManifest is SqlProviderManifest))
  {
   throw EntityUtil.Argument(...);
  }
 
  ...
 
}

7. DbFunctionExpression IsLambda and LambdaBody properties are now internal

Affects:
       - Provider Writers

SP1 Beta Code:  
In provider code it was possible to inspect the values of the IsLambda and Lambda body parameters, but they would always be false and null, respectively.

 protected override void VisitDbFunctionExpression(
DbFunctionExpression expression)
{ 
if(expression.IsLambda) {
throw EntityUtil.NotSupported(...);
}

GenerateFunctionInvocationSql(expression);
}

SP1 RTM Code:
Any code that was testing the values of these properties is unnecessary.

 protected override void VisitDbFunctionExpression(
DbFunctionExpression expression)
{ 
GenerateFunctionInvocationSql(expression);
}

Entity Services Changes

1. The Metadata keyword in an entity connection string is now required.

Affects:
       - EntityConnection and ObjectContext users

SP1 Beta Behavior:  
In SP1 Beta, initializing an EntityConnection with a connection string that that doesn't have a Metadata keyword would not throw on the ConnectionString setter or constructor, but would throw on calls to Open() and GetMetadataWorkspace().

SP1 RTM Behavior:  
In SP1 RTM, the EntityConnection will throw in the ConnectionString setter or constructor.

2. EntityDataReader no longer overrides Object.GetHashCode() and Object.Equals(Object object)

Affects:
       - EntityDataReader users

SP1 Beta Code:
Object.Equals(Object object)

Users could have compared two data readers for equality.

 Object obj = …
EntityDataReader reader = …

reader.Equals(obj);

Object.GetHashCode()

Users could have used instances of EntityDataReader as keys in data structures relying on hashing algorithms such as a hash table or a dictionary.

 System.Collections.Hashtable hashtable = new Hashtable();
EntityDataReader reader1 = …
hashtable.Add(reader1, "first");

SP1 RTM Code:
Object.Equals(Object object)

Unchanged, but the result would be based on the default implementation.

Object.GetHashCode()

The default implementation of GetHashCode does not guarantee unique return values for different objects. Thus while the same code would run, it would be officially not supported.

Mapping and Metadata Changes

1. Compile time view generation hash has changed

Affects:
       - Users of compile time view generation

Mitigation:
-
Anyone who uses compile time view generation will need to regenerate the views and recompile
         them.

2. SSDL no longer allows periods in Entity, Association, EntityContiner, EntitySet, AssociationSet, and Function names

Affects:
       - Metadata artifact authors

Mitigation:
-
Names need to be changed so that they do not include periods

SP1 Beta Code:

 <EntityType Name="Entity.1">
...
</EntityType>
<EntityContainer Name="Entity.Container">
 <EntitySet Name="Entity.1"
             Entity="My.Namespace.[Entity.1]"/>
</EntityContainer>

SP1 RTM Code:

 <EntityType Name="Entity_1">...</EntityType><EntityContainer Name="Entity_Container"> <EntitySet Name="Entity_1" Table="Entity.1"
             Entity="My.Namespace.Entity_1"/></EntityContainer>

3. MetadataException constructors no longer take DataSpace string

Continue to use the following constructors to construct MetadataException.

 public MetadataException(string message)
public MetadataException(string message, Exception innerException) 

EntityDataSource Changes

1. The IncludePaths property has been renamed to Include

Affects:
       - ASP.NET users

SP1 Beta Code:
The property IncludePaths was used to specify which navigation propreties should be included in the query.

SP1 RTM Code:
The property name has changed to “Include”.

2. Public Methods removed from the EntityDataSourceView class

The following methods were removed from the EntityDataSourceView class

 public String DefaultOrderByClause { get; }
public void DisposeContext();
public static Boolean EnumerableContentEquals( IEnumerable enumerableA,  IEnumerable enumerableB);

3. Event argument class APIs have changed

The event argument classes for the EntityDataSource have been re-architected to provide better usability.

Affects:
   ASP.NET users

  • ContextCreating

          SP1 Beta Code:

 public class EntityDataSourceContextEventArgs : EventArgs
{
 // Methods
 public EntityDataSourceContextEventArgs();

 // Properties
 public ObjectContext Context { get; set; }
}

          SP1 RTM Code:

 public class EntityDataSourceContextCreatingEventArgs : EventArgs
{
 public ObjectContext Context { get; set; }
}

  • ContextCreated

          SP1 Beta Code:

 public class EntityDataSourceStatusEventArgs : EventArgs
{
}

          SP1 RTM Code:

 public class EntityDataSourceContextCreatedEventArgs : EventArgs
{
  public ObjectContext Context { get; }
}

  • ContextDisposing

          SP1 Beta Code:

 public class EntityDataSourceDisposeEventArgs : CancelEventArgs
{
 public object Context { get; }
}

          SP1 RTM Code:

 public class EntityDataSourceContextDisposingEventArgs : CancelEventArgs
{
 public ObjectContext Context { get; }
}

  • Selecting

          SP1 Beta Code:

 public class EntityDataSourceSelectEventArgs : CancelEventArgs
{
 public DataSourceSelectArguments Arguments { get; }
 public ParameterCollection CommandParameters { get; }
 public string CommandText { get; set; }
 public ObjectContext Context { get; }
 public string OrderBy { get; }
 public ParameterCollection OrderByParameters { get; }
 public string Where { get; }
 public ParameterCollection WhereParameters { get; }
}

          SP1 RTM Code:

 public class EntityDataSourceSelectingEventArgs : CancelEventArgs
{
 public EntityDataSource DataSource { get; }
 public DataSourceSelectArguments SelectArguments { get; }
}

  • Selected

          SP1 Beta Code:  
          See EntityDataSourceStatusEventArgs

          SP1 RTM Code:

  public class EntityDataSourceSelectedEventArgs : EventArgs
{
    public ObjectContext Context { get; }
 public Exception Exception { get; }
 public bool ExceptionHandled { get; set; }
 public IEnumerable Results { get; }
 public DataSourceSelectArguments SelectArguments { get; }
 public int TotalRowCount { get; }
}

  • Updating

          SP1 Beta Code:

 public class EntityDataSourceUpdateEventArgs : CancelEventArgs
{
 public Exception Exception { get; }
 public bool ExceptionHandled { get; set; }
 public object NewEntity { get; }
 public object OriginalEntity { get; }
}

          SP1 RTM Code:

 public class EntityDataSourceChangingEventArgs : CancelEventArgs
{
  public ObjectContext Context { get; }
 public object Entity { get; }
 public Exception Exception { get; }
 public bool ExceptionHandled { get; set; }
}

  • Updated

          SP1 Beta Code:
          See EntityDataSourceStatusEventArgs

          SP1 RTM Code:

 public class EntityDataSourceChangedEventArgs : EventArgs
{
 public ObjectContext Context { get; }
 public object Entity { get; }
 public Exception Exception { get; }
 public bool ExceptionHandled { get; set; }
}

  • Inserting

          SP1 Beta Code:

 public class EntityDataSourceInsertEventArgs : CancelEventArgs
{
 public Exception Exception { get; }
 public bool ExceptionHandled { get; set; }
 public object NewEntity { get; set; }
 public IDictionary Values { get; }
}

          SP1 RTM Code:
          See EntityDataSourceChangingEventArgs

  • Inserted

          SP1 Beta Code:
          See EntityDataSourceStatusEventArgs

          SP1 RTM Code:
          See EntityDataSourceChangedEventArgs

  • Deleting

          SP1 Beta Code:

 public class EntityDataSourceDeleteEventArgs : CancelEventArgs
{
 public EntityDataSourceValidationException Exception { get; }
 public bool ExceptionHandled { get; set; }
 public object OriginalEntity { get; }
}

          SP1 RTM Code:
          See EntityDataSourceChangingEventArgs

  • Deleted

          SP1 Beta Code:
          See EntityDataSourceStatusEventArgs

          SP1 RTM Code:
          See EntityDataSourceChangedEventArgs

Tools Changes

1. Namespace generation has changed

Existing EDMX files will be affected as soon as you make a change, open/save/close it, “Run Custom Tool” or move the EDMX file into a project sub-folder.

In SP1 beta the data classes were generated into a separate top level CSDL model namespace and users were forced to import the CLR namespace or fully qualify the class names in order to consume the generated data classes. The following changes were made in the RC based on feedback from users:

  • The Model Namespace specified in the wizard GUI has nothing to do with the CLR namespace of the generated data classes
  • The data classes are generate into a CLR namespace determined by the Visual Studio project system (based project type and the project sub-folder containing the EDMX file)
  • The designer respects the value of the Custom Tool Namespace project item property (if set)

Note: EDMX files in VB projects with an empty root namespace or in App_Code root in ASP.NET website projects continue to have the Beta 1 behavior for generated classes (i.e.  data classes are generated into a separate top level CSDL model namespace)

Code consuming the model is affected as follows:

  1. VB projects with an empty root namespace and ASP.NET website apps with EDMX files in App_Code root: no change since SP1 beta

  2. All other projects where the EDMX file is in the project root:

    a. SP1 Beta C# code example:

     using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using NorthwindModel;
    
    namespace ConsoleApplication1
    {
     class Program
     {
     static void Main(string[] args)
     {
     using (NorthwindEntities entities = 
                           new NorthwindEntities())
     {
     foreach (Customers c in entities.Customers)
     Console.WriteLine(c.ContactName);
     }
     }
     }
    }
    

    b. SP1 RTM C# code example (note we don’t need the “using NorthwindModel;” anymore):

     using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
     class Program
     {
     static void Main(string[] args)
     {
     using (NorthwindEntities entities = 
                           new NorthwindEntities())
     {
     foreach (Customers c in entities.Customers)
     Console.WriteLine(c.ContactName);
     }
     }
     }
    }
    
  3. SP1 RTM C# code example for all projects where the EDMX file is in a project sub-folder (e.g. MyModels). Note the “using ConsoleApplication1.MyModels;”  below:

     PrFont34Bin0BinSub0Frac0Def1Margin0Margin0Jc1Indent1440Lim0Lim1using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using ConsoleApplication1.MyModels;
    
    namespace ConsoleApplication1
    {
     class Program
     {
     static void Main(string[] args)
     {
     using (NorthwindEntities entities = 
                     new NorthwindEntities())
     {
     foreach (Customers c in entities.Customers)
     Console.WriteLine(c.ContactName);
     }
     }
     }
    }
    
  4. In all projects, multiple EDMX files in the same project sub-folder cannot have duplicate entity or container names. Conflicts in generated classes (if any) can be resolved in one of the following ways:

a. For C# projects: Move the EDMX files into different project sub-folders folders or set the “Custom Tool Namespace” to a unique namespace

b. For ASP.NET website projects: Move the EDMX files into different project sub-folders folders under App_Code

c. For VB projects: Set the “Custom Tool Namespace” to a unique namespace