I'm trying to write a "simple" Generic Get<T>; extension for
System.Runtime.MemoryCache.
Why "simple" ? Because generally I know object's real type before caching it, so when I retrieve it from cache, I'm not going to convert it in unpredictable ways.
For example: if boolean "true" value is stored in cache with cacheKey "id", so
Get<string>("id") == "true";
Get<int>("id") == 1; // any result > 0 is okay
Get<SomeUnpredictableType> == null; // just ignore these trouble conversions
Here's my incomplete implemention:
public static T DoGet<T>(this MemoryCache cache, string key) {
object value = cache.Get(key);
if (value == null) {
return default(T);
}
if (value is T) {
return (T)value;
}
// TODO: (I'm not sure if following logic is okay or not)
// 1. if T and value are both numeric type (e.g. long => double), how to code it?
// 2. if T is string, call something like Convert.ToString()
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
Any suggestions are highly appreciated.
===================================
Update (04/11/2016):
For those nice suggestions given, I implement my first version of Get<T>
public class MemCache {
private class LazyObject<T> : Lazy<T> {
public LazyObject(Func<T> valueFactory) : base(valueFactory) { }
public LazyObject(Func<T> valueFactory, LazyThreadSafetyMode mode) : base(valueFactory, mode) { }
}
private static T CastValue<T>(object value) {
if (value == null || value is DBNull) {
return default(T);
}
Type valType = value.GetType();
if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(LazyObject<>)) {
return CastValue<T>(valType.GetProperty("Value").GetValue(value));
}
if (value is T) {
return (T)value;
}
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(t) && typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
private MemoryCache m_cache;
public T Get<T>(string key) {
return CastValue<T>(m_cache.Get(key));
}
public void Set<T>(string key, T value, CacheDependency dependency) {
m_cache.Set(key, value, dependency.AsCacheItemPolicy());
}
public T GetOrAdd<T>(string key, Func<T> fnValueFactory, CacheDependency dependency) {
LazyObject<T> noo = new LazyObject<T>(fnValueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
LazyObject<T> old = m_cache.AddOrGetExisting(key, noo, dependency.AsCacheItemPolicy()) as LazyObject<T>;
try {
return CastValue<T>((old ?? noo).Value);
} catch {
m_cache.Remove(key);
throw;
}
}
/* Remove/Trim ... */
}
The essential work is to write a CastValue<T> to convert any object to desired type. And it doesn't have to handle very complicate condition because object types in cache is predictable for the programmer. And here's my version.
public static T CastValue<T>(object value) {
if (value == null || value is DBNull) {
return default(T);
}
if (value is T) {
return (T)value;
}
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(t) && typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
Proposal:
public static T DoGet<T>(this MemoryCache cache, string key)
{
object value = cache.Get(key);
if (value == null) {
return default(T);
}
// support for nullables. Do not waste performance with
// type conversions if it is not a nullable.
var underlyingType = Nullable.GetUnderlyingType(t);
if (underlyingType != null)
{
value = Convert.ChangeType(value, underlyingType);
}
return (T)value;
}
Usage (supposed you have an id of type int in the cache):
int id = Get<int>("id");
int? mayBeId = Get<int?>("id");
string idAsString = Get<int?>("id")?.ToString();
double idAsDouble = (double)Get<int>("id");
I haven't test it.
Related
I have problem with CustomType in Fluent NHibernate.
I have to save as Json a dictionary to my database.
I created a customType for this that implements IUserType interface.
There is no problem to save dictionary to database for first time, but when i try to update collection, nHibernate dont set a property as dirty and dont update it.
My Equals and GetHashCode methods
public new bool Equals(object x, object y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
if (!(x is IDictionary<K, Z>) || !(y is IDictionary<K, Z>))
return false;
var dic1 = (x as IDictionary<K, Z>).OrderBy(z=> z.Key);
var dic2 = (y as IDictionary<K, Z>).OrderBy(z => z.Key);
return dic1.SequenceEqual(dic2);
}
public int GetHashCode(object x)
{
if (x == null)
return 0;
return x.GetHashCode();
}
The objects passed to equals method are always the same (recently modified) two object.
Anyone have a idea what i'm doing wrong ?
Rest of IUserType implementation code:
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
if (names.Length != 1)
throw new InvalidOperationException("Only expecting one column...");
var val = rs[names[0]] as string;
if (val != null && !string.IsNullOrWhiteSpace(val))
{
return JsonConvert.DeserializeObject<T>(val, JSonSerializableTypeSerializer.Settings);
}
return null;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var parameter = (DbParameter)cmd.Parameters[index];
if (value == null)
{
parameter.Value = DBNull.Value;
}
else
{
parameter.Value = JsonConvert.SerializeObject(value, JSonSerializableTypeSerializer.Settings);
}
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
public SqlType[] SqlTypes
{
get
{
return new SqlType[] { new StringClobSqlType() };
}
}
public Type ReturnedType
{
get { return typeof(T); }
}
public bool IsMutable
{
get { return true; }
}
Maybe I am wrong, but I guess you have returned false while implementing IUserType.IsMutable.
Since your user type returns a Dictionary, which is a mutable type, and which you do indeed mutate, your user type must return true for its IsMutable property.
Using Mono.Cecil, given this method
private Instruction LoadOnStack(MetadataType type, object value)
{
switch (type)
{
case MetadataType.String:
return _processor.Create(OpCodes.Ldstr, (string) value);
case MetadataType.Int32:
return _processor.Create(OpCodes.Ldc_I4, (Int32) value);
case MetadataType.Int64:
return _processor.Create(OpCodes.Ldc_I8, (Int64) value);
case MetadataType.Boolean:
return _processor.Create(OpCodes.Ldc_I4, (bool) value ? 1 : 0);
}
throw new NotSupportedException("Not a supported primitve parameter type: " + type);
}
How can I create an Instruction that can load value, when value is of type Type?
I notice when value is of type Type that I can test it for it like so :
if (value is TypeReference)
return _processor.Create(???, ???);
But I can not figure out what I need to pass to Create to get the value to load correctly.
EDIT:
Using this :
if (value is TypeReference)
return _processor.Create(OpCodes.Ldobj, type.Resolve());
Gets me one step closer. It seems to to accept the type. But then when I try to write the assembly, it errors out saying :
System.ArgumentException : Member 'System.Type' is declared in another module and needs to be imported
As #cubrr already pointed it out:
We use this code for MethodBoundaryAspect.Fody
private IList<Instruction> LoadValueOnStack(TypeReference parameterType, object value, ModuleDefinition module)
{
if (parameterType.IsPrimitive || (parameterType.FullName == "System.String"))
return new List<Instruction> {LoadPrimitiveConstOnStack(parameterType.MetadataType, value)};
if (parameterType.IsValueType) // enum
{
var enumUnderlyingType = GetEnumUnderlyingType(parameterType.Resolve());
return new List<Instruction> {LoadPrimitiveConstOnStack(enumUnderlyingType.MetadataType, value)};
}
if (parameterType.FullName == "System.Type")
{
var typeName = value.ToString();
var typeReference = module.GetType(typeName, true);
var typeTypeRef = _referenceFinder.GetTypeReference(typeof (Type));
var methodReference = _referenceFinder.GetMethodReference(typeTypeRef, md => md.Name == "GetTypeFromHandle");
var instructions = new List<Instruction>
{
_processor.Create(OpCodes.Ldtoken, typeReference),
_processor.Create(OpCodes.Call, methodReference)
};
return instructions;
}
throw new NotSupportedException("Parametertype: " + parameterType);
}
private Instruction LoadPrimitiveConstOnStack(MetadataType type, object value)
{
switch (type)
{
case MetadataType.String:
return _processor.Create(OpCodes.Ldstr, (string) value);
case MetadataType.Int32:
return _processor.Create(OpCodes.Ldc_I4, (int) value);
case MetadataType.Int64:
return _processor.Create(OpCodes.Ldc_I8, (long) value);
case MetadataType.Boolean:
return _processor.Create(OpCodes.Ldc_I4, (bool) value ? 1 : 0);
}
throw new NotSupportedException("Not a supported primitive parameter type: " + type);
}
private static TypeReference GetEnumUnderlyingType(TypeDefinition self)
{
foreach (var field in self.Fields)
{
if (field.Name == "value__")
return field.FieldType;
}
throw new ArgumentException();
}
where class ReferenceFinder is:
private readonly ModuleDefinition _moduleDefinition;
public ReferenceFinder(ModuleDefinition moduleDefinition)
{
_moduleDefinition = moduleDefinition;
}
public MethodReference GetMethodReference(Type declaringType, Func<MethodDefinition, bool> predicate)
{
return GetMethodReference(GetTypeReference(declaringType), predicate);
}
public MethodReference GetMethodReference(TypeReference typeReference, Func<MethodDefinition, bool> predicate)
{
var typeDefinition = typeReference.Resolve();
MethodDefinition methodDefinition;
do
{
methodDefinition = typeDefinition.Methods.FirstOrDefault(predicate);
typeDefinition = typeDefinition.BaseType == null
? null
: typeDefinition.BaseType.Resolve();
} while (methodDefinition == null && typeDefinition != null);
return _moduleDefinition.Import(methodDefinition);
}
public TypeReference GetTypeReference(Type type)
{
return _moduleDefinition.Import(type);
}
Hopefully a simple enough question, but I am struggling to find an answer.
How can I cast an 'object' to a type defined in a 'Type' object.
For example, basically I am trying to do the following with reflection, making it as generic as possible:
public class MyClass
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
}
public T FillMyClass<T>() where T : new()
{
//The definitions here are suppled in code - each class we want to work with needs to be "mapped".
string name1 = "Prop1";
Type type1 = typeof(string);
string name2 = "Prop2";
Type type2 = typeof(int);
//The values always start out as a string because of the way I'm receiving it.
string val1 = "test";
string val2 = "1";
T t= new T();
//Works fine, because val1 is already a string
t.GetType().GetProperty(name1).SetValue(t, val1, null);
//Having trouble with the below.
object o = Convert.ChangeType(val2, type2);
//Fails because o is not an int
t.GetType().GetProperty(name2).SetValue(t, o, null);
}
So the type is defined by the user (or possibly even just by looking up the type of the property).
But I just can't see how to cast the object to a [Type].
I've come across this issue as well a couple of times. While I'm sure there is a more elegant solution to this problem, I've made the following extension method to get you 99% of the way there.
public static object TryConvertToType(this object source, Type destinationType, object defaultValue = null)
{
try
{
if (source == null)
return defaultValue;
if (destinationType == typeof(bool))
{
bool returnValue = false;
if (!bool.TryParse(source.ToString(), out returnValue))
{
return Convert.ChangeType(source.ToString() == "1", destinationType);
}
else
{
return Convert.ChangeType(returnValue, destinationType);
}
}
else if (destinationType.IsSubclassOf(typeof(Enum)))
{
try
{
return Enum.Parse(destinationType, source.ToString());
}
catch
{
return Enum.ToObject(destinationType, source);
}
}
else if (destinationType == typeof(Guid))
{
return Convert.ChangeType(new Guid(source.ToString().ToUpper()), destinationType);
}
else if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
Type genericType = destinationType.GetGenericArguments().First();
return Convert.ChangeType(source, genericType);
}
else if (source.GetType().IsSubclassOf(destinationType))
{
return Convert.ChangeType(source, destinationType);
}
else if (!source.GetType().IsValueType
&& source.GetType() != typeof(string)
&& destinationType == typeof(string))
{
return Convert.ChangeType(source.GetType().Name, destinationType);
}
else
{
return Convert.ChangeType(source, destinationType);
}
}
catch
{
return defaultValue;
}
}
Basically, in usage, it works like so:
t.GetType().GetProperty(name).GetValue(t, null).TryConvertToType(type2, 0);
I'm trying to run the following code but get a casting error.
How can I rewrite my code to achive the same ?
boolResult= (bool?)dataReader["BOOL_FLAG"] ?? true;
intResult= (int?)dataReader["INT_VALUE"] ?? 0;
Thanks
Use the "IsDbNull" method on the data reader... for example:
bool? result = dataReader.IsDbNull(dataReader["Bool_Flag"]) ? null : (bool)dataReader["Bool_Flag"]
Edit
You'd need to do something akin to:
bool? nullBoolean = null;
you'd have
bool? result = dataReader.IsDbNull(dataReader["Bool_Flag"]) ? nullBoolean : (bool)dataReader["Bool_Flag"]
Consider doing it in a function.
Here's something I used in the past (you can make this an extension method in .net 4):
public static T GetValueOrDefault<T>(SqlDataReader dataReader, System.Enum columnIndex)
{
int index = Convert.ToInt32(columnIndex);
return !dataReader.IsDBNull(index) ? (T)dataReader.GetValue(index) : default(T);
}
Edit
As an extension (not tested, but you get the idea), and using column names instead of index:
public static T GetValueOrDefault<T>(this SqlDataReader dataReader, string columnName)
{
return !dataReader.IsDBNull(dataReader[columnName]) ? (T)dataReader.GetValue(dataReader[columnName]) : default(T);
}
usage:
bool? flag = dataReader.GetValueOrDefault("BOOL_COLUMN");
There's an answer here that might be helpful:
https://stackoverflow.com/a/3308515/1255900
You can use the "as" keyword. Note the caution mentioned in the comments.
nullableBoolResult = dataReader["BOOL_FLAG"] as bool?;
Or, if you are not using nullables, as in your original post:
boolResult = (dataReader["BOOL_FLAG"] as bool?) ?? 0;
bool? boolResult = null;
int? intResult = null;
if (dataReader.IsDBNull(reader.GetOrdinal("BOOL_FLAG")) == false)
{
boolResult = dataReader.GetBoolean(reader.GetOrdinal("BOOL_FLAG"));
}
else
{
boolResult = true;
}
if (dataReader.IsDBNull(reader.GetOrdinal("INT_VALUE")) == false)
{
intResult= dataReader.GetInt32(reader.GetOrdinal("INT_VALUE"));
}
else
{
intResult = 0;
}
I'm sure I found the inspiration for this somewhere around the interweb but I can't seem to find the original source anymore. Anyway, below you find a utility class which allows to define an extension method on DataReader, like this:
public static class DataReaderExtensions
{
public static TResult Get<TResult>(this IDataReader reader, string name)
{
return reader.Get<TResult>(reader.GetOrdinal(name));
}
public static TResult Get<TResult>(this IDataReader reader, int c)
{
return ConvertTo<TResult>.From(reader[c]);
}
}
Usage:
reader.Get<bool?>("columnname")
or
reader.Get<int?>(5)
Here's the enabling utility class:
public static class ConvertTo<T>
{
// 'Factory method delegate', set in the static constructor
public static readonly Func<object, T> From;
static ConvertTo()
{
From = Create(typeof(T));
}
private static Func<object, T> Create(Type type)
{
if (!type.IsValueType) { return ConvertRefType; }
if (type.IsNullableType())
{
return (Func<object, T>)Delegate.CreateDelegate(typeof(Func<object, T>), typeof(ConvertTo<T>).GetMethod("ConvertNullableValueType", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new[] { type.GetGenericArguments()[0] }));
}
return ConvertValueType;
}
// ReSharper disable UnusedMember.Local
// (used via reflection!)
private static TElem? ConvertNullableValueType<TElem>(object value) where TElem : struct
{
if (DBNull.Value == value) { return null; }
return (TElem)value;
}
// ReSharper restore UnusedMember.Local
private static T ConvertRefType(object value)
{
if (DBNull.Value != value) { return (T)value; }
return default(T);
}
private static T ConvertValueType(object value)
{
if (DBNull.Value == value)
{
throw new NullReferenceException("Value is DbNull");
}
return (T)value;
}
}
EDIT: makes use of the IsNullableType() extension method defined like so:
public static bool IsNullableType(this Type type)
{
return
(type.IsGenericType && !type.IsGenericTypeDefinition) &&
(typeof (Nullable<>) == type.GetGenericTypeDefinition());
}
Here's my shot at an extension method. Column name semantics and falls back to default(T) when a null is encountered.
public static class DbExtensions
{
public static T ReadAs<T>(this IDataReader reader, string col)
{
object val = reader[col];
if (val is DBNull)
{
// Use the default if the column is null
return default(T);
}
return (T)val;
}
}
Here is the sample usage. Remember that despite string being a reference type, it will still fail to cast to null from a DBNull. The same is true with int?.
public Facility Bind(IDataReader reader)
{
var x = new Facility();
x.ID = reader.ReadAs<Guid>("ID");
x.Name = reader.ReadAs<string>("Name");
x.Capacity = reader.ReadAs<int?>("Capacity");
x.Description = reader.ReadAs<string>("Description");
x.Address = reader.ReadAs<string>("Address");
return x;
}
using extension method:
public static T GetValueOrDefault <T> (this SqlDataReader reader, string column) {
var isDbNull = reader[column] == DBNull.Value;
return !isDbNull ? (T) reader[column] : default (T);
}
Remember that a DBNull is not the same thing as null, so you cannot cast from one to the other. As the other poster said, you can check for DBNull using the IsDBNull() method.
Try this version. It performs some basic conversion and manages default values as well.
I am trying to combine a bunch of similar methods into a generic method. I have several methods that return the value of a querystring, or null if that querystring does not exist or is not in the correct format. This would be easy enough if all the types were natively nullable, but I have to use the nullable generic type for integers and dates.
Here's what I have now. However, it will pass back a 0 if a numeric value is invalid, and that unfortunately is a valid value in my scenarios. Can somebody help me out? Thanks!
public static T GetQueryString<T>(string key) where T : IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
What if you specified the default value to return, instead of using default(T)?
public static T GetQueryString<T>(string key, T defaultValue) {...}
It makes calling it easier too:
var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified
The downside being you need magic values to denote invalid/missing querystring values.
I know, I know, but...
public static bool TryGetQueryString<T>(string key, out T queryString)
What about this? Change the return type from T to Nullable<T>
public static Nullable<T> GetQueryString<T>(string key) where T : struct, IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
Convert.ChangeType() doesn't correctly handle nullable types or enumerations in .NET 2.0 BCL (I think it's fixed for BCL 4.0 though). Rather than make the outer implementation more complex, make the converter do more work for you. Here's an implementation I use:
public static class Converter
{
public static T ConvertTo<T>(object value)
{
return ConvertTo(value, default(T));
}
public static T ConvertTo<T>(object value, T defaultValue)
{
if (value == DBNull.Value)
{
return defaultValue;
}
return (T) ChangeType(value, typeof(T));
}
public static object ChangeType(object value, Type conversionType)
{
if (conversionType == null)
{
throw new ArgumentNullException("conversionType");
}
// if it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
// null input returns null output regardless of base type
if (value == null)
{
return null;
}
// it's a nullable type, and not null, which means it can be converted to its underlying type,
// so overwrite the passed-in conversion type with this underlying type
conversionType = Nullable.GetUnderlyingType(conversionType);
}
else if (conversionType.IsEnum)
{
// strings require Parse method
if (value is string)
{
return Enum.Parse(conversionType, (string) value);
}
// primitive types can be instantiated using ToObject
else if (value is int || value is uint || value is short || value is ushort ||
value is byte || value is sbyte || value is long || value is ulong)
{
return Enum.ToObject(conversionType, value);
}
else
{
throw new ArgumentException(String.Format("Value cannot be converted to {0} - current type is " +
"not supported for enum conversions.", conversionType.FullName));
}
}
return Convert.ChangeType(value, conversionType);
}
}
Then your implementation of GetQueryString<T> can be:
public static T GetQueryString<T>(string key)
{
T result = default(T);
string value = HttpContext.Current.Request.QueryString[key];
if (!String.IsNullOrEmpty(value))
{
try
{
result = Converter.ConvertTo<T>(value);
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
You can use sort of Maybe monad (though I'd prefer Jay's answer)
public class Maybe<T>
{
private readonly T _value;
public Maybe(T value)
{
_value = value;
IsNothing = false;
}
public Maybe()
{
IsNothing = true;
}
public bool IsNothing { get; private set; }
public T Value
{
get
{
if (IsNothing)
{
throw new InvalidOperationException("Value doesn't exist");
}
return _value;
}
}
public override bool Equals(object other)
{
if (IsNothing)
{
return (other == null);
}
if (other == null)
{
return false;
}
return _value.Equals(other);
}
public override int GetHashCode()
{
if (IsNothing)
{
return 0;
}
return _value.GetHashCode();
}
public override string ToString()
{
if (IsNothing)
{
return "";
}
return _value.ToString();
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
public static explicit operator T(Maybe<T> value)
{
return value.Value;
}
}
Your method would look like:
public static Maybe<T> GetQueryString<T>(string key) where T : IConvertible
{
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
return new Maybe<T>();
}
}
return new Maybe<T>();
}
I like to start with a class like this
class settings
{
public int X {get;set;}
public string Y { get; set; }
// repeat as necessary
public settings()
{
this.X = defaultForX;
this.Y = defaultForY;
// repeat ...
}
public void Parse(Uri uri)
{
// parse values from query string.
// if you need to distinguish from default vs. specified, add an appropriate property
}
This has worked well on 100's of projects. You can use one of the many other parsing solutions to parse values.