sapiens/SqlFu

Custom logic for getting and setting properties

Closed this issue · 4 comments

This is a really cool lib. I was wondering if there is a way to configure custom logic for getting and setting properties? The reason I am asking is because I was hoping to use this lib with F#, and while most things work out of the box, F# option types seem to be a sticking point. I think that to support the F# option type it might be necessary to provide custom getter and setter rules so that the developer can plugin the logic necessary to go from an Option to a T or a Nullable when writing to the database and from a T or Nullable to an Option when reading from the database. Is there a way that I might be able to do this?

Have you tried with a custom converter? My F# knowledge is limited so the example is with c#

 SqlFuManager.Configure(c =>
            {
                c.RegisterConverter(obj=>new OptionalType(obj as int));
                   
            });

It should work when mapping from row to object. I don't have a way to use a custom converter for property to column, although nullables should be handled correctly by sqlfu.

RegisterConverter looked promising for getting the value from the row but it looks like there might be some work happening in between getting the data from the data reader and making use of the converter. I get a System.ArgumentNullException when trying to read a null int column with a Converter for type Option<>.

System.ArgumentNullException: Value cannot be null.
Parameter name: constructor
   at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, IEnumerable`1 arguments)
   at System.Reflection.TypeFactory.GetFactory(Type t)
   at SqlFu.Mapping.Internals.Mapper`1.Map(DbDataReader reader, String parentPrefix)
>    at SqlFu.SqlFuManager.<>c__DisplayClass15_0`1.<GetMapper>b__0(DbDataReader reader)
   at SqlFu.DbCommandExtensions.QueryAndProcess[T](DbCommand cmd, Func`2 processor, Func`2 mapper, Boolean firstRowOnly)
   at SqlFu.FluentCommandBuilder`1.GetFirstRow()
   at SqlFu.DbConnectionExtensions.QueryRow[T](DbConnection db, Func`2 builder)

Additionally, Option type values do not get populated into the column during insert or update.

Regardless, it's a really well thought out lib!

I see. I think the problem is it searches for a parameterless constructor and perhaps F# Option doesn't implement that. Maybe a solution would be adding a way to allow the user to choose which mapper should be used for a certain type.

I'll think about it. It's important for me to make SqlFu as flexible as possible.

Ok, looks like I lead you astray a bit. Your remark about the parameterless constructor reminded me of something and it turns out the issue was not the converter or the option type per se. It was that I was using an F# Record type instead of an F# Class type as the POCO and that was what did not have a default constructor. Fortunately, by adding the CLIMutable attribute to the definition of the Record type it works anyway just like a Class type would so no need to give up the semantics of working with Records in F# in order to work with SqlFu. I still have some more to fiddling to do with writes to see if I can get a better idea of what is or is not happening but at least I have a partial solution as I can confirm that the Converters do make the needed difference when reading from the database. Thanks for following up and I'll let you know how far I get.