Commit fb5dd93b authored by Marc Gravell's avatar Marc Gravell

Merge pull request #197 from kevin-montrose/master

whack at an explicit constructor attribute
parents 8e33a56d 9846b9af
......@@ -389,6 +389,15 @@ public interface ITypeMap
/// <returns>Matching constructor or default one</returns>
ConstructorInfo FindConstructor(string[] names, Type[] types);
/// <summary>
/// Returns a constructor which should *always* be used.
///
/// Parameters will be default values, nulls for reference types and zero'd for value types.
///
/// Use this class to force object creation away from parameterless constructors you dn't control.
/// </summary>
ConstructorInfo FindExplicitConstructor();
/// <summary>
/// Gets mapping for constructor parameter
/// </summary>
......@@ -3557,6 +3566,44 @@ public static void SetTypeMap(Type type, ITypeMap map)
types[i - startBound] = reader.GetFieldType(i);
}
var explicitConstr = typeMap.FindExplicitConstructor();
if (explicitConstr != null)
{
var structLocals = new Dictionary<Type, LocalBuilder>();
var consPs = explicitConstr.GetParameters();
foreach(var p in consPs)
{
if(!p.ParameterType.IsValueType)
{
il.Emit(OpCodes.Ldnull);
}
else
{
LocalBuilder loc;
if(!structLocals.TryGetValue(p.ParameterType, out loc))
{
structLocals[p.ParameterType] = loc = il.DeclareLocal(p.ParameterType);
}
il.Emit(OpCodes.Ldloca, (short)loc.LocalIndex);
il.Emit(OpCodes.Initobj, p.ParameterType);
il.Emit(OpCodes.Ldloca, (short)loc.LocalIndex);
il.Emit(OpCodes.Ldobj, p.ParameterType);
}
}
il.Emit(OpCodes.Newobj, explicitConstr);
il.Emit(OpCodes.Stloc_1);
supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
}
}
else
{
var ctor = typeMap.FindConstructor(names, types);
if (ctor == null)
{
......@@ -3569,15 +3616,18 @@ public static void SetTypeMap(Type type, ITypeMap map)
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_1);
supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
if(supportInitialize)
if (supportInitialize)
{
il.Emit(OpCodes.Ldloc_1);
il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
}
}
else
{
specializedConstructor = ctor;
}
}
}
il.BeginExceptionBlock();
if (type.IsValueType)
......@@ -5216,6 +5266,22 @@ public ConstructorInfo FindConstructor(string[] names, Type[] types)
return null;
}
/// <summary>
/// Returns the constructor, if any, that has the ExplicitConstructorAttribute on it.
/// </summary>
public ConstructorInfo FindExplicitConstructor()
{
var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var withAttr = constructors.Where(c => c.GetCustomAttributes(typeof(ExplicitConstructorAttribute), true).Length > 0).ToList();
if (withAttr.Count == 1)
{
return withAttr[0];
}
return null;
}
/// <summary>
/// Gets mapping for constructor parameter
/// </summary>
......@@ -5306,6 +5372,15 @@ public ConstructorInfo FindConstructor(string[] names, Type[] types)
return _type.GetConstructor(new Type[0]);
}
/// <summary>
/// Always returns null
/// </summary>
/// <returns></returns>
public ConstructorInfo FindExplicitConstructor()
{
return null;
}
/// <summary>
/// Not impelmeneted as far as default constructor used for all cases
/// </summary>
......@@ -5544,6 +5619,15 @@ public interface IWrappedDataReader : IDataReader
IDbCommand Command { get; }
}
/// <summary>
/// Tell Dapper to use an explicit constructor, passing nulls or 0s for all parameters
/// </summary>
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)]
public sealed class ExplicitConstructorAttribute : Attribute
{
}
// Define DAPPER_MAKE_PRIVATE if you reference Dapper by source
// and you like to make the Dapper types private (in order to avoid
// conflicts with other projects that also reference Dapper by source)
......
......@@ -4086,6 +4086,43 @@ public void Issue192_InParameterWorksWithSimilarNamesWithUnicode()
((int)rows.Field).IsEqualTo(2);
((int)rows.Field_1).IsEqualTo(2);
}
class _ExplicitConstructors
{
public int Field { get; set; }
public int Field_1 { get; set; }
private bool WentThroughProperConstructor;
public _ExplicitConstructors() { }
[ExplicitConstructor]
public _ExplicitConstructors(string foo, int bar)
{
WentThroughProperConstructor = true;
}
public bool GetWentThroughProperConstructor()
{
return WentThroughProperConstructor;
}
}
public void ExplicitConstructors()
{
var rows = connection.Query<_ExplicitConstructors>(@"
declare @ExplicitConstructors table (
Field INT NOT NULL PRIMARY KEY IDENTITY(1,1),
Field_1 INT NOT NULL);
insert @ExplicitConstructors(Field_1) values (1);
SELECT * FROM @ExplicitConstructors"
).ToList();
rows.Count.IsEqualTo(1);
rows[0].Field.IsEqualTo(1);
rows[0].Field_1.IsEqualTo(1);
rows[0].GetWentThroughProperConstructor().IsTrue();
}
#if POSTGRESQL
class Cat
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment