Reputation: 1347
I have a static class in which I am using dictionaries as lookup tables to map between .NET types and SQL types. Here is an example of such a dictionary:
private static readonly Dictionary<Type, string> SqlServerMap = new Dictionary<Type, string>
{
{typeof (Boolean), "bit"},
{typeof (Byte[]), "varbinary(max)"},
{typeof (Double), "float"},
{typeof (Byte), "tinyint"},
{typeof (Int16), "smallint"},
{typeof (Int32), "int"},
{typeof (Int64), "bigint"},
{typeof (Decimal), "decimal"},
{typeof (Single), "real"},
{typeof (DateTime), "datetime2(7)"},
{typeof (TimeSpan), "time"},
{typeof (String), "nvarchar(MAX)"},
{typeof (Guid), "uniqueidentifier"}
};
Then I have a public method below which passes in a .NET type and it returns the string value of the corresponding MS SQL Server type using this dictionary. However, since this is being used as a lookup table for making database queries, I think it makes sense to make it a ConcurrentDictionary
. I changed it to:
private static readonly IDictionary<Type, string> SqlServerMap = new ConcurrentDictionary<Type, string>
{
{typeof (Boolean), "bit"},
{typeof (Byte[]), "varbinary(max)"},
{typeof (Double), "float"},
{typeof (Byte), "tinyint"},
{typeof (Int16), "smallint"},
{typeof (Int32), "int"},
{typeof (Int64), "bigint"},
{typeof (Decimal), "decimal"},
{typeof (Single), "real"},
{typeof (DateTime), "datetime2(7)"},
{typeof (TimeSpan), "time"},
{typeof (String), "nvarchar(MAX)"},
{typeof (Guid), "uniqueidentifier"}
};
But now it underlines everything red within the {}
(i.e. all key value pairs of the ConcurrentDictionary
) and the error is:
Cannot access private method 'Add' here
I don't think it's because I initialize it as private static readonly
, because I just tested by making a public static
version and I got the same error.
Upvotes: 22
Views: 15953
Reputation: 1396
Try this
private static readonly IDictionary<Type, string> SqlServerMap =
new ConcurrentDictionary<Type, string>(
new Dictionary<Type, string>()
{
{typeof(Boolean ), "bit" },
{typeof(Byte[] ), "varbinary(max)" },
{typeof(Double ), "float" },
{typeof(Byte ), "tinyint" },
{typeof(Int16 ), "smallint" },
{typeof(Int32 ), "int" },
{typeof(Int64 ), "bigint" },
{typeof(Decimal ), "decimal" },
{typeof(Single ), "real" },
{typeof(DateTime), "datetime2(7)" },
{typeof(TimeSpan), "time" },
{typeof(String ), "nvarchar(MAX)" },
{typeof(Guid ), "uniqueidentifier"}
}
);
Updated: if you are using C#6(Roslyn 2.0 Compiler), you can use the new Dictionary Initializers.
private static readonly IDictionary<Type, string> SqlServerMap =
new ConcurrentDictionary<Type, string>
{
[typeof(Boolean )] = "bit" ,
[typeof(Byte[] )] = "varbinary(max)" ,
[typeof(Double )] = "float" ,
[typeof(Byte )] = "tinyint" ,
[typeof(Int16 )] = "smallint" ,
[typeof(Int32 )] = "int" ,
[typeof(Int64 )] = "bigint" ,
[typeof(Decimal )] = "decimal" ,
[typeof(Single )] = "real" ,
[typeof(DateTime)] = "datetime2(7)" ,
[typeof(TimeSpan)] = "time" ,
[typeof(String )] = "nvarchar(MAX)" ,
[typeof(Guid )] = "uniqueidentifier"
};
Example https://dotnetfiddle.net/9ZgjsR
Upvotes: 51
Reputation: 31
As your collection is not going to change, you could now use the ImmutableDictionary. While this also has issues with the initialize, there are solutions proposed for this question about initialization:
A simple solution provided by @LukášLánský is
var d = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } }.ToImmutableDictionary();
and a better performing version provided by @IanGriffiths is
public struct MyDictionaryBuilder<TKey, TValue> : IEnumerable
{
private ImmutableDictionary<TKey, TValue>.Builder _builder;
public MyDictionaryBuilder(int dummy)
{
_builder = ImmutableDictionary.CreateBuilder<TKey, TValue>();
}
public void Add(TKey key, TValue value) => _builder.Add(key, value);
public TValue this[TKey key]
{
set { _builder[key] = value; }
}
public ImmutableDictionary<TKey, TValue> ToImmutable() => _builder.ToImmutable();
public IEnumerator GetEnumerator()
{
// Only implementing IEnumerable because collection initializer
// syntax is unavailable if you don't.
throw new NotImplementedException();
}
}
Upvotes: 3
Reputation: 37020
As a code example to Servy's accepted answer, in order to initialize a ConcurrentDictionary
at instantiation, you can pass a type that impements IEnumerable
(like a List
) of KeyValuePair
types to the constructor:
private static readonly IDictionary<Type, string> SqlServerMap =
new ConcurrentDictionary<Type, string>(
new List<KeyValuePair<Type, string>>
{
new KeyValuePair<Type, string>(typeof(Boolean), "bit"),
new KeyValuePair<Type, string>(typeof(Boolean), "bit"),
new KeyValuePair<Type, string>(typeof(Byte[]), "varbinary(max)"),
new KeyValuePair<Type, string>(typeof(Double), "float"),
new KeyValuePair<Type, string>(typeof(Byte), "tinyint"),
new KeyValuePair<Type, string>(typeof(Int16), "smallint"),
new KeyValuePair<Type, string>(typeof(Int32), "int"),
new KeyValuePair<Type, string>(typeof(Int64), "bigint"),
new KeyValuePair<Type, string>(typeof(Decimal), "decimal"),
new KeyValuePair<Type, string>(typeof(Single), "real"),
new KeyValuePair<Type, string>(typeof(DateTime), "datetime2(7)"),
new KeyValuePair<Type, string>(typeof(TimeSpan), "time"),
new KeyValuePair<Type, string>(typeof(String), "nvarchar(MAX)"),
new KeyValuePair<Type, string>(typeof(Guid), "uniqueidentifier")
});
Upvotes: 5
Reputation: 1968
As said @Servy in his answer, collection initialization works for types with Add
method. But also it should work, if extension method with name Add
and appropriate signature exists. So you could create one for concurrent dictionary. Initialization will be thread-safe, due to fact you are using static field initializer.
Upvotes: 0
Reputation: 203802
The collection initializer that you're using to populate the collection only works if the collection has an Add
method of an appropriate signature and accessibility. ConcurrentDictionary
doesn't have a public Add
method, so you won't be able to use a collection initializer with it.
You can provide some initial data by passing an IEnumerable<KeyValuePair<TKey, TValue>>
as a parameter to the constructor, or you can call TryAdd
(or AddOrUpdate
, or any of the other methods with Add
in the name) in a loop after creating the ConcurrentDictionary
.
Upvotes: 17