This project is read-only.

Design Meeting Notes - October 17, 2013

Code First in the EDM wizard

In the future we want the new “ADO.NET Entity Data Model” wizard to work for creating a Code First project. However, the file name that is input in the item template dialog (before the wizard runs) currently assumes that the wizard will create an EDMX file, and so shows the .edmx file extension. With Code First there is no EDMX file and instead the output is a context file (.cs or .vb) and possibly some other source files.

Two options were considered:

  • Keep the existing entry to the wizard the same and have a new item template for Code First
  • Change the current item template to not specify any type of file and then generate either EDMX or a context with the given name depending on what is selected in the wizard

We chose to go with the second option since it better fits the goal of having a single wizard for Code First, Database First, and Model First.

Open-sourcing the EF Tools

Directory structure

As part of making the EF tools open source we need to move the code into the main EF git repository. This is the proposed directory structure, given that it must fit with what we already have, which in turn was required to be consistent with the web stack structure:

  • src\
    • Common\
    • EFTools\
      • setup\
    • EntityFramework\
    • EntityFramework.PowerShell\
    • EntityFramework.PowerShell.Utility\
    • EntityFramework.SqlServer\
    • EntityFramework.SqlServerCompact\
    • Migrate\
    • NuGet\
    • PowerTools\
  • test\
    • EntityFramework\
    • PowerTools.Test\
    • EFTools\
  • tools\
    • VsIdeHostAdapter\

Build commands and solutions

Runtime:

  • The existing build.cmd will remain unchanged—to build and test the runtime only.
  • The existing runtime solution will also remain unchanged

Tools:

  • A new EFToolsBuild.cmd will be added to build and test the EF tools
    • The runtime tests will not be run, although obviously everything is run on the CI machine
  • A new solution will be added that allows the runtime and designer code to be worked on together
    • This will allow refactoring, etc. across all projects

Adding new interceptors

New interceptors around provider-level database operations are needed for two reasons:

  • To implement the workaround for transaction commit failures (see below)
  • To generally allow more database operations to be intercepted without the need for a wrapping provider

The first point is the highest priority and the current focus, but the second point must be kept in mind when deciding what to implement now and how to organize the interceptors, especially with regard to not making breaking changes to the interception interfaces in the future.

New interception methods

Members that could be intercepted for DbTransaction:

DbConnection Connection { get; }
IsolationLevel IsolationLevel { get; }
void Dispose();
void Commit();
void Rollback();

Members that could be intercepted for DbConnection:

string ConnectionString { get; set; }
int ConnectionTimeout { get; }
string Database { get; }
string DataSource { get; }
ConnectionState State { get; }
DbTransaction BeginTransaction(IsolationLevel isolationLevel)
void Close();
DbCommand CreateCommand();
void EnlistTransaction(Transaction transaction);
void Open();
Task OpenAsync()

Additional members that could be intercepted for DbCommand:

string CommandText { get; set; }
int CommandTimeout { get; set; }
CommandType CommandType { get; set; }
DbConnection Connection { get; set; }
DbParameterCollection Parameters { get; }
DbTransaction Transaction { get; set; }
UpdateRowSource UpdatedRowSource { get; set; }
DbParameter CreateParameter()
void Prepare();
DbProviderFactory ProviderFactory { get; }

In general we will add interceptors for all these methods and properties. Some notes:

  • If adding interception for a given member turns out to be very hard or problematic and it doesn’t seem like the member is very useful to intercept, then we may choose to exclude it at this time.
  • Object creation is interesting because there are often multiple different ways in which some of these objects can be created. For example, a DbCommand can be created with DbConnection.CreateCommand, but it is more common for it to be created through cloning of an existing command when a connection is not even available. For this reason we will put creation interception methods on the class associated with the object that is being created, just like we have done for DbCommandTree already.
  • We already have an IDbCommandInterceptor interface but we need to now add additional methods to this interface. This would be a breaking change that we don’t want to make, so we will instead create a new interface that derives from the existing interface and adds the new methods. Naming still to be determined—for now we will probably go with IDbCommandInterceptor2
    • Interception for parameter creation will not go on this interface because it is the creation case mentioned above and so should go on IDbParameterInterceptor, if and when that gets created.
  • We will use natural before and after naming where possible in a similar way to the Executed and Executing methods that we already have. Some initial ideas for names (may change in API review):
    • Committing/Committed
    • RollingBack/RolledBack
    • DataSourceSetting/DataSourceSet
    • DataSourceGetting/DataSourceGot

Use of interception contexts

Should we continue with the pattern of putting call parameters as properties in the interception context?

  • Pros:
    • Consistent with what we already have
    • Allows the values to be changed in the before call without the use of ref parameters for both reference and value types
    • Easier to support new overloads or many overloads without many interception methods
  • Cons:
    • Results in either lots of different interception context classes, or classes that share a lot of properties some of which are not used for a given method
    • May be less discoverable

Decision: we will continue putting the parameters in the contexts and create new context classes where necessary to avoid lots of unused properties.

Should we create derived interception context types even if these types currently contain no additional properties? Yes, because otherwise it would be a breaking change to the interface if we want to add properties to the context in the future, and one of the main reasons for having the context is to allow properties to be added in the future without it being a breaking change.

Workaround for commit failures

This is in reference to the following work item: https://entityframework.codeplex.com/workitem/1114

The initial plan is:

  • Implement with a connection/transaction interceptor
  • On BeginTransaction assign a unique ID to the transaction and writes it to the database
    • May try to have Code First/Migrations support for creating the database table
  • On commit
    • If an exception is thrown, then check the table for the transaction ID and re-throw if not found
    • If no exception, then delete the transaction ID from the table
  • Changing the transaction table name might be through a static property
  • Shipped as part of SqlClient provider

Additional feedback from meeting:

  • Try to do this in a provider agnostic way since all ADO.NET providers have this problem and the workaround seems to be reasonable in general
  • Measure perf impact of enabling this workaround and determine if any provider-specific optimization necessary
  • Perform the check for ID in an execution strategy to allow for retries
  • Consider not deleting the transaction ID every time but instead using a timestamp and occasionally purging old values

The mechanism for enabling the workaround is still being investigated. It may depend on whether it is provider agnostic of specific.

Last edited Oct 18, 2013 at 9:49 PM by ajcvickers, version 2