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 DerstadtEntity Framework Developer
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
4. DbCommandTree and DbExpression no longer have a Clone method
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
IList<DbExpression> arg = ((DbNaryExpression)exp).Arguments;
IList<DbExpression> arg = ((DbArithmeticExpression)exp).Arguments;
6. ProviderManifest.Provider and ProviderManifest.Token properties have been removed
protected override DbCommandDefinition CreateDbCommandDefinition( DbProviderManifest providerManifest, DbCommandTree commandTree) { if(!SqlProviderManifest.ProviderInvariantName.Equals( providerManifest.Provider, StringComparison.Ordinal)) { throw EntityUtil.Argument(...); } ... }
protected override DbCommandDefinition CreateDbCommandDefinition( DbProviderManifest providerManifest, DbCommandTree commandTree) { if(!(providerManifest is SqlProviderManifest)) { throw EntityUtil.Argument(...); } ... }
7. DbFunctionExpression IsLambda and LambdaBody properties are now internal
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.
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
<EntityType Name="Entity.1"> ... </EntityType> <EntityContainer Name="Entity.Container"> <EntitySet Name="Entity.1" Entity="My.Namespace.[Entity.1]"/> </EntityContainer>
<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
public class EntityDataSourceContextEventArgs : EventArgs { // Methods public EntityDataSourceContextEventArgs(); // Properties public ObjectContext Context { get; set; } }
public class EntityDataSourceContextCreatingEventArgs : EventArgs { public ObjectContext Context { get; set; } }
public class EntityDataSourceStatusEventArgs : EventArgs { }
public class EntityDataSourceContextCreatedEventArgs : EventArgs { public ObjectContext Context { get; } }
public class EntityDataSourceDisposeEventArgs : CancelEventArgs { public object Context { get; } }
public class EntityDataSourceContextDisposingEventArgs : CancelEventArgs { public ObjectContext Context { get; } }
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; } }
public class EntityDataSourceSelectingEventArgs : CancelEventArgs { public EntityDataSource DataSource { get; } public DataSourceSelectArguments SelectArguments { get; } }
SP1 Beta Code: See EntityDataSourceStatusEventArgs
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; } }
public class EntityDataSourceUpdateEventArgs : CancelEventArgs { public Exception Exception { get; } public bool ExceptionHandled { get; set; } public object NewEntity { get; } public object OriginalEntity { get; } }
public class EntityDataSourceChangingEventArgs : CancelEventArgs { public ObjectContext Context { get; } public object Entity { get; } public Exception Exception { get; } public bool ExceptionHandled { get; set; } }
public class EntityDataSourceChangedEventArgs : EventArgs { public ObjectContext Context { get; } public object Entity { get; } public Exception Exception { get; } public bool ExceptionHandled { get; set; } }
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
SP1 RTM Code: See EntityDataSourceChangedEventArgs
public class EntityDataSourceDeleteEventArgs : CancelEventArgs { public EntityDataSourceValidationException Exception { get; } public bool ExceptionHandled { get; set; } public object OriginalEntity { get; } }
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:
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:
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); } } } }
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); } } } }
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