J'utilise des types d'unions similaires aux énumérations sur mes objets dapper:
type Confidence =
| Low
| Medium
| High
type Goal = {
Confidence: Confidence
...
}
J'ai créé un gestionnaire de type personnalisé afin de le faire fonctionner:
type UnionHandler<'T>() =
inherit SqlMapper.TypeHandler<'T>()
override __.SetValue(param, value) =
param.Value <- value.ToString()
()
override x.Parse(value: obj) =
Union.parse <| string value
let registerTypeHandlers() =
SqlMapper.AddTypeHandler (UnionHandler<Confidence>())
Cela fonctionne bien, mais ce serait encore mieux si je n'avais pas à en enregistrer un nouveau pour chaque nouveau type d'union.
Est-il possible de rendre le gestionnaire de types générique de telle sorte qu'il puisse gérer tous les types d'unions avec un seul enregistrement?
Cela peut être fait avec Reflection:
let internal addUnionTypeHandlers() =
let assembly = Assembly.GetExecutingAssembly()
let unionHandlerType =
assembly.GetTypes()
|> Seq.filter(fun t -> t.Name.Contains("UnionHandler") && t.IsGenericTypeDefinition)
|> Seq.head
assembly.GetTypes()
|> Seq.filter(fun t -> not t.IsGenericType && FSharpType.IsUnion(t, BindingFlags.Default))
|> Seq.iter(fun t ->
let ctor = unionHandlerType
.MakeGenericType(t)
.GetConstructor(Array.empty)
.Invoke(Array.empty)
(typeof<SqlMapper>.GetMethods()
|> Seq.filter(fun methodInfo ->
if methodInfo.Name = "AddTypeHandler" && methodInfo.IsGenericMethodDefinition then
let gp = methodInfo.GetParameters()
not <| isNull gp && gp.Length = 1 && gp.[0].ParameterType.Name.Contains("TypeHandler")
else false)
|> Seq.head)
.MakeGenericMethod(t)
.Invoke(null, [| ctor |]) |> ignore
)
Remarque:
Cela aurait été beaucoup plus simple si Dapper avait eu la signature d'AddTypeHandler sous la forme ITypeHandler -> unit. Mais il accepte TypeHandler et a en outre une version surchargée. Nous avons donc besoin de GMD pour la méthode AddTypeHandler et l'instancions avec la méthode MakeGenericMethod, puis appelons cette méthode avec le paramètre que nous obtenons de GetConstructor ... Invoke
En jouant plus loin avec la réflexion, vous pouvez décider de marquer certaines unions discriminées avec un attribut pour ignorer l'ajout du mappage. Vous pouvez étendre le code pour analyser si le type a un attribut. Vous pouvez également effectuer des manipulations sur la base du module, je suppose en utilisant FSharpType.IsModule