Regarding changeset e (update logging/interception based on design meeting decisions):
-
Any chance we could use DateTime.UtcNow instead of DateTime.Now in the logging? Seems like we're in "database code", and I'm strongly opinionated about the database and UTC :)
-
While the delegate Action<string> instead of TextWriter is a definite improvement, I still feel this is one layer too close to the iron as it still forces the logging to occur as string. This still forces the formatting of the trace into the framework, and still forces string conversions even when the logging might not be retained or persistence in a more structured form is desired. I will submit a pull with a different view of how this could be done.
-
The cloning of Result and IsResultSet in the copy-constructor for DbCommandInterceptionContext<TResult> and DbCommandTreeInterceptionContext should probably be coded in terms of the IDbInterceptionContextWithResult<TResult> interface instead of a cast to the concrete type (where, for DbCommandTreeInterceptionContext, TResult is DbCommandTree). This is especially evident given the Dispatch methods are constrained by the base DbInterceptionContext and the interfaceIDbInterceptionContextWithResult<TResult>.
-
In InternalDispatcher.cs, the Dispatch<TInterceptionContext, TResult>( TResult result, ... variant seems to have non-obvious side effect of NOT setting the interceptionContext.Result to result if there are no _interceptors registered. While that's like not a bad thing, it might be a surprise to callers of Dispatch that we expecting the interceptionContext.Result to contain the result.
-
This same "didn't set it issue" is also true in the Func<TResult> operation version. It SEEMS like the main intent is to not incur the .Each call and the possible aggregation/copy-down of the interceptionContext.Result. It also seems that the interceptionContext passed to the registered interceptors when an exception has been caught will have the unchanged/priorResult value from the caller (which might be surprising).
public TResult Dispatch<TInterceptionContext, TResult>(
TResult result,
TInterceptionContext interceptionContext,
Action<TInterceptor> intercept)
where TInterceptionContext : DbInterceptionContext, IDbInterceptionContextWithResult<TResult>
{
interceptionContext.Result = result;
if (_interceptors.Count != 0)
{
_interceptors.Each(intercept);
}
return interceptionContext.Result;
}
public TResult Dispatch<TInterceptionContext, TResult>(
Func<TResult> operation,
TInterceptionContext interceptionContext,
Action<TInterceptor> executing,
Action<TInterceptor, TInterceptionContext> executed)
where TInterceptionContext : DbInterceptionContext, IDbInterceptionContextWithResult<TResult>
{
// should we set interceptionContext.Result = default(TResult);
bool hasInterceptors = _interceptors.Count > 0;
if (hasInterceptors)
{
_interceptors.Each(executing);
}
try
{
if (!interceptionContext.IsResultSet)
{
interceptionContext.Result = operation();
}
}
catch (Exception ex)
{
if (hasInterceptors)
{
interceptionContext = (TInterceptionContext)interceptionContext.WithException(ex);
_interceptors.Each(i => executed(i, interceptionContext));
}
throw;
}
if (hasInterceptors)
{
_interceptors.Each(i => executed(i, interceptionContext));
}
return interceptionContext.Result;
}