I am working on access a database using F# and my initial attempt at creating a function to create the update query is flawed.
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) =
let buf = new System.Text.StringBuilder("UPDATE users SET ");
if (oldUser.FirstName.Equals(newUser.FirstName) = false) then buf.Append("SET first_name='").Append(newUser.FirstName).Append("'" ) |> ignore
if (oldUser.LastName.Equals(newUser.LastName) = false) then buf.Append("SET last_name='").Append(newUser.LastName).Append("'" ) |> ignore
if (oldUser.UserName.Equals(newUser.UserName) = false) then buf.Append("SET username='").Append(newUser.UserName).Append("'" ) |> ignore
buf.Append(" WHERE id=").Append(newUser.Id).ToString()
This doesn't properly put a ,
between any update parts after the first, for example:
UPDATE users SET first_name='Firstname', last_name='lastname' WHERE id=...
I could put in a mutable variable to keep track when the first part of the set
clause is appended, but that seems wrong.
I could just create an list of tuples, where each tuple is oldtext, newtext, columnname, so that I could then loop through the list and build up the query, but it seems that I should be passing in a StringBuilder
to a recursive function, returning back a boolean
which is then passed as a parameter to the recursive function.
Does this seem to be the best approach, or is there a better one?
UPDATE:
Here is what I am using as my current solution, as I wanted to make it more generalized, so I just need to write an abstract class for my entities to derive from and they can use the same function. I chose to split up how I do the function so I can pass in how to create the SET
part of the update so I can test with different ideas.
let BuildUserUpdateQuery3 (oldUser:UserType) (newUser:UserType) =
let properties = List.zip3 oldUser.ToSqlValuesList newUser.ToSqlValuesList oldUser.ToSqlColumnList
let init = false, new StringBuilder()
let anyChange, (formatted:StringBuilder) =
properties |> Seq.fold (fun (anyChange, sb) (oldVal, newVal, name) ->
match(oldVal=newVal) with
| true -> anyChange, sb
| _ ->
match(anyChange) with
| true -> true, sb.AppendFormat(",{0} = '{1}'", name, newVal)
| _ -> true, sb.AppendFormat("{0} = '{1}'", name, newVal)
) init
formatted.ToString()
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) (updatequery:UserType->UserType->String) =
let buf = StringBuilder("UPDATE users SET ");
buf.AppendFormat(" {0} WHERE id={1}", (updatequery oldUser newUser), newUser.Id)
let UpdateUser conn (oldUser:UserType) (newUser:UserType) =
let query = BuildUserUpdateQuery oldUser newUser BuildUserUpdateQuery3
execNonQuery conn (query.ToString())