fsprojects / FSharp.Data.SqlClient

A set of F# Type Providers for statically typed access to MS SQL database
http://fsprojects.github.io/FSharp.Data.SqlClient/
Other
205 stars 71 forks source link

Allow another way to pass database context #107

Closed smoothdeveloper closed 9 years ago

smoothdeveloper commented 9 years ago

currently, the connection context can be any of those:

  type Connection =
      | Literal of string
      | NameInConfig of string
      | Transaction of SqlTransaction

In code base I work with (which is not in F#), it's more convenient to pass Func around which takes care of creating dbcommand with appropriate timeout, connection and transaction depending application context, this was introduced in the code base to replace a lot of manual connection / transaction management and keeping around the desired timeout value.

I'd like to allow this to work with F# SqlClient (so I can introduce it's usage in the codebase I work with) and wondering if there is interest in this?

After a quick look, the changes seems to be restricted to RuntimeSqlCommand implementation but there are probably several ways to deal with this.

The simplest way seems to

   | CreateCommandFunctor of (unit -> SqlCommand) 

If this seems interesting and appropriate I can draft implementation, please provide feedback.

Thanks!

dmitry-a-morozov commented 9 years ago

In general you're absolutely right but normal coding rules unfortunately cannot be applied to type providers implementation. An issue with your proposal that it will require to construct CreateCommandFunctor lambda function inside quotations. Most likely here https://github.com/fsprojects/FSharp.Data.SqlClient/blob/master/src/SqlClient/SqlCommandProvider.fs#L164. Which will make it impossible to debug. Considering that RuntimeSqlCommand constructor is not something that user deals with directly this trade-off probably doesn't make sense. I know that without actual type providers development experience you can be skeptical about what I said. The best way to validate is to code this idea. So you're welcome to try but I warned you.

smoothdeveloper commented 9 years ago

@dmitry-a-morozov thanks for your feedback, I vaguely know TypeProvider authoring is a bit of a headache and I'm not acquainted with the quotation stuff.

I'm not sure I grasp what you mean with the debug consideration, I gave a try at implementing the changes in my fork and it seems to work properly, I also added an extension point to alter the command which is prepared which is also something I need, this is exposed only to the new constructor.

One thing I changed is that AsSqlCommand also carries the timeout setting in the cloned command, I think this was an overlook.

I didn't see any regression in the tests and I added tests covering the changes and expected behaviour, please have a look at the pull request: https://github.com/fsprojects/FSharp.Data.SqlClient/pull/108

Thanks!

dmitry-a-morozov commented 9 years ago

By debuggability I meant a code in between <@@ ... @@> or explicit constructed quotations cannot be debugged. (try to put break-point or step-into this code). The only way to diagnose problems in this code is to use old-fashion printf

smoothdeveloper commented 9 years ago

@dmitry-a-morozov I really need some discussion or that you show us how you can get a provided command instance ready provided a unit - SqlCommand

Given that in recent releases, API evolves toward giving less access to the base type (RuntimeSqlCommand or ISqlCommand) it makes it unclear if having a generic way to setup the commands is going to be supported, right now I have this.

let setupCommandWithContext<'TCommand when 'TCommand :> FSharp.Data.ISqlCommand> (createCommandFunctor: unit -> SqlCommand) (cmd: 'TCommand) =
  let dummyCommand = createCommandFunctor()
  let cmdAsIsqlCommand = cmd :> FSharp.Data.ISqlCommand
  cmdAsIsqlCommand.Raw.CommandTimeout <- dummyCommand.CommandTimeout
  cmdAsIsqlCommand.Raw.Transaction <- dummyCommand.Transaction
  cmdAsIsqlCommand.Raw.Connection <- dummyCommand.Connection
  ()

let createCommandWithContext<'TCommand when 'TCommand :> FSharp.Data.ISqlCommand and 'TCommand : (new : unit -> 'TCommand)> (createCommandFunctor: unit -> SqlCommand) =
  let cmd = (new 'TCommand())
  setupCommandWithContext createCommandFunctor cmd
  cmd

Issues are:

If you don't accept the PR (I can implement it on top of current master) which IMHO doesn't harm anything, and would open opportunities to stop adding more constructor overloads, can you clarify what I'm supposed to do?

My ideal is that:

smoothdeveloper commented 9 years ago

@dmitry-a-morozov I think what I need is addressed nicely with your comment https://github.com/fsprojects/FSharp.Data.SqlClient/commit/072071457dc3394afa8cf97fff4b3b2cc9e8ffc3#commitcomment-9990389

Thanks for keeping up and helping me figure this out.