Skip to content

Commit d9ccc8f

Browse files
fixup LoadDataTypesMap caching and case of missing SQLCLR types
1 parent 819ffac commit d9ccc8f

File tree

1 file changed

+84
-59
lines changed

1 file changed

+84
-59
lines changed

src/SqlClient.DesignTime/SqlClientExtensions.fs

Lines changed: 84 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ type internal TypeInfoPerConnectionStringCache() =
4747
raise (new InvalidOperationException(sprintf "types for connection %s were not retrieved!" connectionString))
4848
)
4949

50-
member x.DoIfConnectionStringNotRegistered f = lock f
50+
member x.DoIfConnectionStringNotRegistered connectionString ifAlreadyDone f =
51+
lock (fun () ->
52+
if x.ContainsConnectionString connectionString then
53+
ifAlreadyDone ()
54+
else
55+
f ()
56+
)
5157

5258
let internal sqlDataTypesCache = new TypeInfoPerConnectionStringCache()
5359
let internal findTypeInfoBySqlEngineTypeId (connStr, system_type_id, user_type_id : int option) =
@@ -500,7 +506,10 @@ type SqlConnection with
500506
]
501507

502508
member internal this.LoadDataTypesMap() =
503-
sqlDataTypesCache.DoIfConnectionStringNotRegistered(fun () ->
509+
sqlDataTypesCache.DoIfConnectionStringNotRegistered
510+
this.ConnectionString
511+
(fun () -> sqlDataTypesCache.GetTypesForConnectionString this.ConnectionString)
512+
(fun () ->
504513
assert (this.State = ConnectionState.Open)
505514

506515
let sqlEngineTypes, tableVariableTypes =
@@ -560,25 +569,28 @@ order by
560569

561570
let getProvidedType name is_user_defined is_table_type system_type_id user_type_id =
562571
match providerTypes.TryGetValue(name) with
563-
| true, value -> value
572+
| true, value -> Some value
564573
| false, _ when is_user_defined && not is_table_type ->
565574
let type_name = sqlEngineTypes.[system_type_id,user_type_id].name
566-
//let system_type_name = t.name
575+
567576
match providerTypes.TryGetValue type_name with
568577
| false, _ ->
569-
let type_name = sqlEngineTypes.[system_type_id,int system_type_id].name
570-
providerTypes.[type_name]
571-
| true, item -> item
578+
match sqlEngineTypes.TryGetValue ((system_type_id,int system_type_id)) with
579+
| false, _ -> None
580+
| true, sqlType ->
581+
let type_name = sqlType.name
582+
Some providerTypes.[type_name]
583+
| true, item -> Some item
572584
| false, _ when is_table_type ->
573-
SqlDbType.Structured, null, false
585+
Some (SqlDbType.Structured, null, false)
574586
| _ -> failwith ("Unexpected type: " + name)
575587

576588
let getProvidedTypeForSqlTypeEntry (x:SqlTypeEntry) = getProvidedType x.name x.is_user_defined x.is_table_type x.system_type_id x.user_type_id
577589

578590
let rec makeColumn column =
579591
let sqlTypeEntry = sqlEngineTypes.[column.system_type_id, column.user_type_id]
580592
{ Column.Name = column.name
581-
TypeInfo = makeTypeInfo sqlTypeEntry
593+
TypeInfo = Option.get (makeTypeInfo sqlTypeEntry)
582594
Nullable = column.is_nullable
583595
MaxLength = int column.max_length
584596
ReadOnly = column.is_identity || column.is_computed
@@ -590,67 +602,80 @@ order by
590602
Scale = sqlTypeEntry.scale
591603
}
592604
and makeTypeInfo (entry: SqlTypeEntry) =
593-
let sqldbtype, clrTypeFullName, isFixedLength = getProvidedTypeForSqlTypeEntry entry
594-
let tableTypeColumns =
595-
if entry.is_table_type then
596-
tableVariableTypes.[entry.user_type_id] |> Array.map makeColumn
597-
else
598-
Array.empty
599-
{
600-
TypeName = entry.name
601-
Schema = entry.schema_name
602-
SqlEngineTypeId = int entry.system_type_id
603-
UserTypeId = entry.user_type_id
604-
SqlDbType = sqldbtype
605-
IsFixedLength = isFixedLength
606-
ClrTypeFullName = clrTypeFullName
607-
UdttName = if entry.is_table_type then entry.name else ""
608-
TableTypeColumns = tableTypeColumns
609-
}
605+
match getProvidedTypeForSqlTypeEntry entry with
606+
| None -> None
607+
| Some (sqldbtype, clrTypeFullName, isFixedLength) ->
608+
let tableTypeColumns =
609+
if entry.is_table_type then
610+
tableVariableTypes.[entry.user_type_id] |> Array.map makeColumn
611+
else
612+
Array.empty
613+
Some {
614+
TypeName = entry.name
615+
Schema = entry.schema_name
616+
SqlEngineTypeId = int entry.system_type_id
617+
UserTypeId = entry.user_type_id
618+
SqlDbType = sqldbtype
619+
IsFixedLength = isFixedLength
620+
ClrTypeFullName = clrTypeFullName
621+
UdttName = if entry.is_table_type then entry.name else ""
622+
TableTypeColumns = tableTypeColumns
623+
}
610624

611625
let typeInfosForTableTypes =
612626
sqlEngineTypes.Values
613-
|> Seq.map (fun i -> i.user_type_id, i)
627+
|> Seq.choose (fun i ->
628+
match makeTypeInfo i with
629+
| Some typeInfo -> Some (i.user_type_id, (i, typeInfo))
630+
| None ->
631+
// fails when, for example, a SQLCLR type definition is on the DDL but the assembly is missing from the database.
632+
// example: AdventureWorks2012 backup in the test folder tSQLt.Private
633+
// ignoring such types
634+
None
635+
)
614636
|> dict
615637

616638
let typeInfos = [|
617639

618640
for { name = name; system_type_id = system_type_id; user_type_id = user_type_id; is_table_type = is_table_type; schema_name = schema_name; is_user_defined = is_user_defined } in sqlEngineTypes.Values do
619-
let providerdbtype, clrTypeFullName, isFixedLength = getProvidedType name is_user_defined is_table_type system_type_id user_type_id
641+
match getProvidedType name is_user_defined is_table_type system_type_id user_type_id with
642+
| None -> ()
643+
| Some (providerdbtype, clrTypeFullName, isFixedLength) ->
620644

621-
let columns =
622-
if is_table_type
623-
then
624-
let columns = tableVariableTypes.[user_type_id]
625-
columns
626-
|> Array.map
627-
(fun column ->
628-
let typeInfo = typeInfosForTableTypes.[user_type_id]
629-
{ Column.Name = column.name
630-
TypeInfo = makeTypeInfo typeInfo
631-
Nullable = column.is_nullable
632-
MaxLength = int column.max_length
633-
ReadOnly = column.is_identity || column.is_computed
634-
Identity = column.is_identity
635-
PartOfUniqueKey = false
636-
DefaultConstraint = null
637-
Description = null
638-
Precision = typeInfo.precision
639-
Scale = typeInfo.scale }
640-
)
645+
let columns =
646+
if is_table_type then
647+
let columns = tableVariableTypes.[user_type_id]
648+
649+
columns
650+
|> Array.map (fun column ->
651+
let sqlTypeInfo, typeInfo = typeInfosForTableTypes.[user_type_id]
652+
{
653+
Column.Name = column.name
654+
TypeInfo = typeInfo
655+
Nullable = column.is_nullable
656+
MaxLength = int column.max_length
657+
ReadOnly = column.is_identity || column.is_computed
658+
Identity = column.is_identity
659+
PartOfUniqueKey = false
660+
DefaultConstraint = null
661+
Description = null
662+
Precision = sqlTypeInfo.precision
663+
Scale = sqlTypeInfo.scale
664+
})
641665
else
642666
Array.empty
643-
yield {
644-
TypeName = name
645-
Schema = schema_name
646-
SqlEngineTypeId = int system_type_id
647-
UserTypeId = user_type_id
648-
SqlDbType = providerdbtype
649-
IsFixedLength = isFixedLength
650-
ClrTypeFullName = clrTypeFullName
651-
UdttName = if is_table_type then name else ""
652-
TableTypeColumns = columns
653-
}
667+
yield
668+
{
669+
TypeName = name
670+
Schema = schema_name
671+
SqlEngineTypeId = int system_type_id
672+
UserTypeId = user_type_id
673+
SqlDbType = providerdbtype
674+
IsFixedLength = isFixedLength
675+
ClrTypeFullName = clrTypeFullName
676+
UdttName = if is_table_type then name else ""
677+
TableTypeColumns = columns
678+
}
654679
|]
655680
sqlDataTypesCache.RegisterTypes(this.ConnectionString, typeInfos)
656681
typeInfos

0 commit comments

Comments
 (0)