Commit b68362f5 authored by Sam Saffron's avatar Sam Saffron

Merge in changes

parents c394845d 11f393d5
......@@ -42,9 +42,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="EntityFramework">
<HintPath>Dependencies\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
......
......@@ -30,11 +30,8 @@ private static void Setup()
using (var connection = new SqlCeConnection(connectionString))
{
connection.Open();
var sql =
@"
create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null)
";
connection.Execute(sql);
connection.Execute(@" create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) ");
connection.Execute(@" create table Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) ");
}
Console.WriteLine("Created database");
}
......
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlServerCe;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Dapper.Contrib.Extensions;
namespace Dapper.Contrib.Tests
{
public interface IUser
......@@ -26,6 +24,13 @@ public class User : IUser
public int Age { get; set; }
}
[Table("Automobiles")]
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Tests
{
private IDbConnection GetOpenConnection()
......@@ -38,7 +43,19 @@ private IDbConnection GetOpenConnection()
return connection;
}
public void TableName()
{
using (var connection = GetOpenConnection())
{
// tests against "Automobiles" table (Table attribute)
connection.Insert(new Car {Name = "Volvo"});
connection.Get<Car>(1).Name.IsEqualTo("Volvo");
connection.Update(new Car() {Id = 1, Name = "Saab"}).IsEqualTo(true);
connection.Get<Car>(1).Name.IsEqualTo("Saab");
connection.Delete(new Car() {Id = 1}).IsEqualTo(true);
connection.Get<Car>(1).IsNull();
}
}
public void TestSimpleGet()
{
......
......@@ -76,55 +76,39 @@ public static T GetInterfaceProxy<T>()
private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder)
{
Type propType = typeof(bool);
FieldBuilder field = typeBuilder.DefineField("_" + "IsDirty", propType, FieldAttributes.Private);
// Generate a public property
PropertyBuilder property =
typeBuilder.DefineProperty("IsDirty",
var propType = typeof(bool);
var field = typeBuilder.DefineField("_" + "IsDirty", propType, FieldAttributes.Private);
var property = typeBuilder.DefineProperty("IsDirty",
PropertyAttributes.None,
propType,
new Type[] { propType });
// The property set and property get methods require a special set of attributes:
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.SpecialName |
MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig;
MethodAttributes GetSetAttr =
MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.SpecialName |
MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig;
// Define the "get" accessor method for current private field.
MethodBuilder currGetPropMthdBldr =
typeBuilder.DefineMethod("get_" + "IsDirty",
GetSetAttr,
// Define the "get" and "set" accessor methods
var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + "IsDirty",
getSetAttr,
propType,
Type.EmptyTypes);
// Intermediate Language stuff...
ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
var currGetIL = currGetPropMthdBldr.GetILGenerator();
currGetIL.Emit(OpCodes.Ldarg_0);
currGetIL.Emit(OpCodes.Ldfld, field);
currGetIL.Emit(OpCodes.Ret);
// Define the "set" accessor method for current private field.
MethodBuilder currSetPropMthdBldr =
typeBuilder.DefineMethod("set_" + "IsDirty",
GetSetAttr,
var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + "IsDirty",
getSetAttr,
null,
new Type[] { propType });
// Again some Intermediate Language stuff...
ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
var currSetIL = currSetPropMthdBldr.GetILGenerator();
currSetIL.Emit(OpCodes.Ldarg_0);
currSetIL.Emit(OpCodes.Ldarg_1);
currSetIL.Emit(OpCodes.Stfld, field);
currSetIL.Emit(OpCodes.Ret);
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
MethodInfo getMethod = typeof(IProxy).GetMethod("get_" + "IsDirty");
MethodInfo setMethod = typeof(IProxy).GetMethod("set_" + "IsDirty");
var getMethod = typeof(IProxy).GetMethod("get_" + "IsDirty");
var setMethod = typeof(IProxy).GetMethod("set_" + "IsDirty");
typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);
typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);
......@@ -133,42 +117,34 @@ private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder)
private static void CreateProperty<T>(TypeBuilder typeBuilder, string propertyName, Type propType, MethodInfo setIsDirtyMethod, bool isIdentity)
{
FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propType, FieldAttributes.Private);
// Generate a public property
PropertyBuilder property =
typeBuilder.DefineProperty(propertyName,
//Define the field and the property
var field = typeBuilder.DefineField("_" + propertyName, propType, FieldAttributes.Private);
var property = typeBuilder.DefineProperty(propertyName,
PropertyAttributes.None,
propType,
new Type[] { propType });
// The property set and property get methods require a special set of attributes:
MethodAttributes GetSetAttr =
MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.HideBySig;
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.HideBySig;
// Define the "get" accessor method for current private field.
MethodBuilder currGetPropMthdBldr =
typeBuilder.DefineMethod("get_" + propertyName,
GetSetAttr,
// Define the "get" and "set" accessor methods
var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName,
getSetAttr,
propType,
Type.EmptyTypes);
// Intermediate Language stuff...
ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
var currGetIL = currGetPropMthdBldr.GetILGenerator();
currGetIL.Emit(OpCodes.Ldarg_0);
currGetIL.Emit(OpCodes.Ldfld, field);
currGetIL.Emit(OpCodes.Ret);
// Define the "set" accessor method for current private field.
MethodBuilder currSetPropMthdBldr =
typeBuilder.DefineMethod("set_" + propertyName,
GetSetAttr,
var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
getSetAttr,
null,
new Type[] { propType });
// Again some Intermediate Language stuff...
ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
//store value in private field and set the isdirty flag
var currSetIL = currSetPropMthdBldr.GetILGenerator();
currSetIL.Emit(OpCodes.Ldarg_0);
currSetIL.Emit(OpCodes.Ldarg_1);
currSetIL.Emit(OpCodes.Stfld, field);
......@@ -177,23 +153,19 @@ private static void CreateProperty<T>(TypeBuilder typeBuilder, string propertyNa
currSetIL.Emit(OpCodes.Call, setIsDirtyMethod);
currSetIL.Emit(OpCodes.Ret);
//TODO: Should copy all attributes defined by the interface?
if (isIdentity)
{
Type keyAttribute = typeof(KeyAttribute);
// Create a Constructorinfo object for attribute 'MyAttribute1'.
ConstructorInfo myConstructorInfo = keyAttribute.GetConstructor(new Type[] { });
// Create the CustomAttribute instance of attribute of type 'MyAttribute1'.
CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(myConstructorInfo, new object[] { });
var keyAttribute = typeof(KeyAttribute);
var myConstructorInfo = keyAttribute.GetConstructor(new Type[] { });
var attributeBuilder = new CustomAttributeBuilder(myConstructorInfo, new object[] { });
property.SetCustomAttribute(attributeBuilder);
}
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
MethodInfo getMethod = typeof(T).GetMethod("get_" + propertyName);
MethodInfo setMethod = typeof(T).GetMethod("set_" + propertyName);
var getMethod = typeof(T).GetMethod("get_" + propertyName);
var setMethod = typeof(T).GetMethod("set_" + propertyName);
typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);
typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);
}
......
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
......@@ -15,6 +16,7 @@ public static class SqlMapperExtensions
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> GetQueries = new ConcurrentDictionary<RuntimeTypeHandle, string>();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeTableName = new ConcurrentDictionary<RuntimeTypeHandle, string>();
private static IEnumerable<PropertyInfo> KeyPropertiesCache(Type type)
{
......@@ -73,13 +75,12 @@ private static IEnumerable<PropertyInfo> TypePropertiesCache(Type type)
throw new DataException("Get<T> only supports en entity with a [Key] property");
var onlyKey = keys.First();
var name = type.Name;
if (type.IsInterface && name.StartsWith("I"))
name = name.Substring(1);
var name = GetTableName(type);
// TODO: pluralizer
// TODO: query information schema and only select fields that are both in information schema and underlying class / interface
sql = "select * from " + name + "s where " + onlyKey.Name + " = @id";
sql = "select * from " + name + " where " + onlyKey.Name + " = @id";
GetQueries[type.TypeHandle] = sql;
}
......@@ -112,24 +113,44 @@ private static IEnumerable<PropertyInfo> TypePropertiesCache(Type type)
return obj;
}
private static string GetTableName(Type type)
{
string name;
if (!TypeTableName.TryGetValue(type.TypeHandle, out name))
{
name = type.Name + "s";
if (type.IsInterface && name.StartsWith("I"))
name = name.Substring(1);
//NOTE: This as dynamic trick should be able to handle both our own Table-attribute as well as the one in EntityFramework
var tableattr = type.GetCustomAttributes(false).Where(attr => attr.GetType().Name == "TableAttribute").SingleOrDefault() as
dynamic;
if (tableattr != null)
name = tableattr.Name;
TypeTableName[type.TypeHandle] = name;
}
return name;
}
/// <summary>
/// Inserts an entity into table "Ts" and returns identity id.
/// </summary>
/// <param name="connection">Open SqlConnection</param>
/// <param name="entityToInsert">Entity to insert</param>
/// <returns>Identity of inserted entity</returns>
public static long Insert<T>(this IDbConnection connection, T entityToInsert)
public static long Insert<T>(this IDbConnection connection, T entityToInsert) where T : class
{
using (var tx = connection.BeginTransaction())
{
var name = entityToInsert.GetType().Name;
var type = typeof(T);
var name = GetTableName(type);
var sb = new StringBuilder(null);
sb.AppendFormat("insert into {0}s (", name);
sb.AppendFormat("insert into {0} (", name);
var allProperties = TypePropertiesCache(typeof(T));
var keyProperties = KeyPropertiesCache(typeof(T));
var allProperties = TypePropertiesCache(type);
var keyProperties = KeyPropertiesCache(type);
for (var i = 0; i < allProperties.Count(); i++)
{
......@@ -166,7 +187,7 @@ public static long Insert<T>(this IDbConnection connection, T entityToInsert)
/// <param name="connection">Open SqlConnection</param>
/// <param name="entityToUpdate">Entity to be updated</param>
/// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
public static bool Update<T>(this IDbConnection connection, T entityToUpdate)
public static bool Update<T>(this IDbConnection connection, T entityToUpdate) where T : class
{
var proxy = entityToUpdate as IProxy;
if (proxy != null)
......@@ -180,12 +201,10 @@ public static bool Update<T>(this IDbConnection connection, T entityToUpdate)
if (keyProperties.Count() == 0)
throw new ArgumentException("Entity must have at least one [Key] property");
var name = type.Name;
if (type.IsInterface && name.StartsWith("I"))
name = name.Substring(1);
var name = GetTableName(type);
var sb = new StringBuilder();
sb.AppendFormat("update {0}s set ", name);
sb.AppendFormat("update {0} set ", name);
var allProperties = TypePropertiesCache(type);
var nonIdProps = allProperties.Where(a => !keyProperties.Contains(a));
......@@ -216,7 +235,7 @@ public static bool Update<T>(this IDbConnection connection, T entityToUpdate)
/// <param name="connection">Open SqlConnection</param>
/// <param name="entityToDelete">Entity to delete</param>
/// <returns>true if deleted, false if not found</returns>
public static bool Delete<T>(this IDbConnection connection, T entityToDelete)
public static bool Delete<T>(this IDbConnection connection, T entityToDelete) where T : class
{
var type = typeof(T);
......@@ -224,12 +243,10 @@ public static bool Delete<T>(this IDbConnection connection, T entityToDelete)
if (keyProperties.Count() == 0)
throw new ArgumentException("Entity must have at least one [Key] property");
var name = type.Name;
if (type.IsInterface && name.StartsWith("I"))
name = name.Substring(1);
var name = GetTableName(type);
var sb = new StringBuilder();
sb.AppendFormat("delete from {0}s where ", name);
sb.AppendFormat("delete from {0} where ", name);
for (var i = 0; i < keyProperties.Count(); i++)
{
......@@ -242,4 +259,14 @@ public static bool Delete<T>(this IDbConnection connection, T entityToDelete)
return deleted > 0;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class TableAttribute : Attribute
{
public TableAttribute(string tableName)
{
Name = tableName;
}
public string Name { get; private set; }
}
}
......@@ -2,7 +2,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
namespace Dapper.Contrib
namespace Dapper.Contrib.Extensions
{
public static class TypeExtension
{
......
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