Design Meeting Notes – December 5, 2013

New Database.Log content

The addition of transaction and connection interceptors allow new content to be included in the default output created by DatabaseLogFormatter and used for Database.Log output.

Proposal is to use interception of the items in bold:

DbTransaction:

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

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()

to create output for:

  • Opening and closing connections
  • Starting, committing, and rolling back of transactions

Proposed output:

Connection opened at 11/18/2013 4:45:21 PM -08:00
Transaction started at 11/18/2013 4:45:21 PM -08:00
INSERT INTO Products
VALUES (value1, value2)
-- Param1: 'value1' (Type = String)
-- Param2: 'value2' (Type = String)
-- Executing at 11/18/2013 4:45:21 PM -08:00
-- Completed in 77 ms with result: 2
Transaction committed 90 ms after being started
Transaction rolled back 90 ms after being started
Connection closed 101 ms after being opened

Notes:

  • These events will only be logged when the operations are done with or by EF. We may do more work later to extend this. Other components can also dispatch to interceptors to trigger logging.
  • It may be better to timestamp on committed/rolled back/closed rather than printing elapsed time
    • We should be careful about not leaking timers
  • Text may be massaged a bit to optimize wording and readability
  • Output is for a single context so correlation of connection/transaction not critical because there will be one at a time.

Designer “White Tests”

In order to open–source the designer, we also needed an open–source test suite. This needed to be lightweight, easy to understand, and easy to run. The legacy suite was closed–source (it used Maui and other custom frameworks), making it difficult to open–source and hard to understand. Initially the suite will include:

  • Unit and functional tests (these make up the bulk of the test suite).
  • A handful of End–to–End UI automation tests (comprising the most important customer usage scenarios).

The approach was:

  • Identified core designer customer End–to–End scenarios and automated these using UI automation.
  • Initially used Windows Automation which ships with .NET. Two proof of concept test cases were created.
  • Final test suite was created using the White UI automation framework. White is an open–source library which builds on top of Windows automation.

The current state is:

  • Identified five core customer scenarios that need UI automation.
  • Four of these scenarios are checked in and run as build.cmd on dev machines:
    • Run through the wizard, creating an empty model.
    • Run through the wizard, generating a model from database.
    • Run through the wizard, generating a model from database, and changing default values.
    • Update model from database.
  • The final scenario is close to completion:
    • Verify every UI dialog available in designer.

Next steps:

  • Start running tests nightly using MadDog.
  • Wrap up final scenario
  • Get a CI machine up and running for the new EFTools repo

Index attribute

For CodePlex item 57 we need to define an Attribute class for use in defining indexes when building a Code First model. This attribute:

  • Is placed on class properties to include them in an index
  • Will result in an index in the SQL created by Migrations for the columns that a property maps to
  • Multiple column properties can be created by placing attributes with the same index name on multiple columns

Examples:

public class Entity1 
{ 
    // Single column, non-clustered, non-unique index with a generated name
    [Index] 
    public int Property { get; set; } 
} 

public class Entity2 
{ 
    // Single column with a generated name and options specified
    [Index(IsClustered = true, IsUnique = true)] 
    public int Property { get; set; } 
}

public class Entity3 
{ 
    // Named, single column, non-clustered, non-unique
    [Index("MyIndex")] 
    public int Property { get; set; } 
}

public class Entity4 
{ 
    // Named, single column, with options specified
    [Index("MyIndex", IsClustered = true, IsUnique = true)] 
    public int Property { get; set; } 
}

public class Entity5 
{ 
    // Multi-column, non-clustered, non-unique
    // Single index with multiple columns created because index names match
    [Index("MyIndex", 0)] 
    public int PropertyA { get; set; }

    [Index("MyIndex", 1)] 
    public int PropertyB { get; set; } 
}

public class Entity6 
{ 
    // Multi-column with options  // Options for an index can be specified once or many times but must be consistent
    [Index("MyIndex", 0, IsClustered = true, IsUnique = true)] 
    public int PropertyA { get; set; }

    [Index("MyIndex", 1, IsClustered = true, IsUnique = true)] 
    public int PropertyB { get; set; } 
} 

public class Entity7 
{ 
    // Multi-column with options specified only once
    // Options for an index can be specified once or many times but must be consistent
    [Index("MyIndex", 0, IsClustered = true, IsUnique = true)] 
    public int PropertyA { get; set; }

    [Index("MyIndex", 1)] 
    public int PropertyB { get; set; } 
} 

public class Entity8 
{ 
    // Multi-column with conflicting options; will throw
    // Options for an index can be specified once or many times but must be consistent
    [Index("MyIndex", 0, IsClustered = true, IsUnique = true)] 
    public int PropertyA { get; set; }

    [Index("MyIndex", 1, IsClustered = true, IsUnique = false)] 
    public int PropertyB { get; set; } 
}
 
// Multiple classes mapping to the same table (e.g. TPH, splitting) with one index
// Single index with multiple columns created because index names match
public class Entity9A 
{
    [Index("MyIndex", 0)] 
    public int PropertyA { get; set; } 
}
 
public class Entity9B 
{
    [Index("MyIndex", 1)] 
    public int PropertyB { get; set; } 
}

Last edited Dec 6, 2013 at 5:09 PM by ajcvickers, version 2