-

Last edited Feb 7, 2013 at 9:08 PM by ajcvickers, version 1

Comments

JamesHiggins Feb 15, 2013 at 4:46 PM 
Thanks for the reply Arthur.

I'm a software architect, so I tend to get excited about software designs. I was surprised and quite happy to see this kind of internal design detail posted publicly for a Microsoft product! Would have been difficult not to comment since ideas popped into my head. ;)

Interfaces are a really nice tool and allow for a lot more flexibility than classes. Coming from Delphi I was always a bit sad that the VCL was created before interfaces were added and thus rarely used them. I've always found it strange when the .NET FCLs don't use them more and, in particular, the IDb* ones that exist but seem to be unused. Oh well, figured it would be preferable to use an existing interface that matched your goals but that isn't always possible.

Great job of EF by the way. I've been using ORM frameworks for years and even wrote my own in the past. EF still has some room for improvement (what doesn't?) but its excellent and makes development much easier and more productive.

ajcvickers Feb 15, 2013 at 1:23 AM 
Hi James,

Thanks for taking the time to comment on these notes. With regard to using IDbTransaction, we avoid using this interface (and other similar interfaces) because of the way the .NET Framework uses these interfaces, or, I should say, doesn’t use these interfaces. That is to say that most .NET APIs accept and return instances of the concrete type rather than the interface, and this makes it a painful and often futile experience to try to program against the interfaces. There is also another related but internal reason why we can’t create new API that uses these interfaces, but unfortunately I can’t talk publicly about that reason.

With regard to using the resolver to obtain a transaction, this is an interesting idea. I will bring it up with some others on the team and see what they think.

With regard to passing the transaction to the constructor, this is certainly a nice pattern…except for the fact that DbContext already has a lot of constructors and the choice already causes a certain amount of confusion. We don’t really want to add to the concept count or constructor count here. You should still be able to use the pattern in your example by defining the constructor on your class appropriately. For example:

public class MyContext : DbContext
{
public MyContext(DbTransaction transaction)
: base(transaction.Connection)
{
this.Database.UseTransaction(transaction);
}
}

Thanks,
Arthur

JamesHiggins Feb 14, 2013 at 8:47 PM 
Darn, sorry the code didn't show up properly. I haven't posted on CodePlex much and reading in "Get Help" I saw that {code:c#} should have resulted in proper formatting. Apparently that does not apply to comments.

JamesHiggins Feb 14, 2013 at 8:45 PM 
Its great that you're posting design meeting notes publicly. I had a couple of thoughts regarding the transaction discussion from 1/31 and 2/7.

Regarding what class to return and what to name it, how about returning the already existing IDbTransaction interface? Its signature matches and its aptly named, plus using an interface instead of a class enables flexibility.


"Note that there are cases with these where EF could attempt to use the transaction after it has been disposed."
If this is a concern IDbTransaction could be expanded. No good suggestion on name, IDbConnectionTransaction is a possibility or the more generic IDbTransaction2 similar to how Direct-X handles names. The implementation would update State when Rollback or Commit was called.

{code:c#}
public enum DbTransactionState { Pending, Committed, Rolledback }
public interface IDbTransaction2 : IDbTransaction
{
DbTransactionState State { get; }
}
{code:c#}


Obtaining transactions could also be implemented as a service using your new IDbDependencyResolver. Pass in IDbTransaction as the type and the EF context needing the transaction as the key. The root resolver would mirror existing EF functionality and users could add their own resolver if they needed customized transaction handling. For example a non-standard isolation level could be applied that would then be used automatically by EF whenever it started a transaction. By checking the

{code:c#}
public class MyTransactionResolver : IDbDependencyResolver
{
public object GetService(Type type, object key)
{
if (type == typeof(IDbConnectionTransaction))
{
if (key is MyEntities)
return (key as ObjectContext).Connection.BeginTransaction(IsolationLevel.Snapshot));
if (key is MyOtherEntities)
return (key as ObjectContext).Connection.BeginTransaction(IsolationLevel.Chaos));

// Allows root resolver to create a standard transaction for other context types
}
}
}
{code:c#}


Personally I'd like the option of passing the transaction into the context constructor as an overload instead of the connection. The Connection property of IDbTransaction specifies the connection, so if a transaction is provided the connection can be omitted.

{code:c#}
using (var connection = new SqlConnection(connectionString))
{
connection.Open();

using (var transaction = connection.BeginTransaction())
{
// Do some stuff with connection and transaction

using (var context = new MyContext(transaction))
{
// Do EF stuff--the transaction should be used
}

using (var context = new MyContext(transaction))
{
// Do EF stuff--the transaction should be used
}
}
}
{code:c#}