提交 c4905c57 编写于 作者: J Johan Danforth

New features in Dapper.Contrib

上级 24e55162
......@@ -44,7 +44,7 @@ public async Task TestSimpleGetAsync()
{
var id = await connection.InsertAsync(new User { Name = "Adama", Age = 10 });
var user = await connection.GetAsync<User>(id);
user.Id.IsEqualTo((int)id);
user.TheId.IsEqualTo((int)id);
user.Name.IsEqualTo("Adama");
await connection.DeleteAsync(user);
}
......@@ -90,7 +90,7 @@ public async Task InsertCheckKeyAsync()
(await connection.GetAsync<IUser>(3)).IsNull();
User user = new User { Name = "Adamb", Age = 10 };
int id = (int)await connection.InsertAsync(user);
user.Id.IsEqualTo(id);
user.TheId.IsEqualTo(id);
}
}
......@@ -102,9 +102,9 @@ public async Task BuilderSelectClauseAsync()
var data = new List<User>();
for (int i = 0; i < 100; i++)
{
var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() };
var nU = new User { Age = rand.Next(70), TheId = i, Name = Guid.NewGuid().ToString() };
data.Add(nU);
nU.Id = (int)await connection.InsertAsync<User>(nU);
nU.TheId = (int)await connection.InsertAsync<User>(nU);
}
var builder = new SqlBuilder();
......@@ -118,8 +118,8 @@ public async Task BuilderSelectClauseAsync()
foreach (var u in data)
{
if (!ids.Any(i => u.Id == i)) throw new Exception("Missing ids in select");
if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age)) throw new Exception("Missing users in select");
if (!ids.Any(i => u.TheId == i)) throw new Exception("Missing ids in select");
if (!users.Any(a => a.TheId == u.TheId && a.Name == u.Name && a.Age == u.Age)) throw new Exception("Missing users in select");
}
}
}
......
......@@ -17,6 +17,14 @@ public static void IsEqualTo<T>(this T obj, T other)
}
}
public static void IsMoreThan(this int obj, int other)
{
if (obj < other)
{
throw new ApplicationException(string.Format("{0} should be larger than {1}", obj, other));
}
}
public static void IsSequenceEqualTo<T>(this IEnumerable<T> obj, IEnumerable<T> other)
{
if (!obj.SequenceEqual(other))
......
using System.Data;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Transactions;
using Dapper.Contrib.Extensions;
using System.Collections.Generic;
using System;
namespace Dapper.Contrib.Tests
{
......@@ -90,10 +89,83 @@ public void TestSimpleGet()
}
}
public void InsertList()
{
const int numberOfEntities = 100;
var users = new List<User>();
for (var i = 0; i < numberOfEntities; i++)
users.Add(new User { Name = "User " + i, Age = i });
using (var connection = GetOpenConnection())
{
connection.DeleteAll<User>();
var total = connection.Insert(users);
total.IsEqualTo(numberOfEntities);
users = connection.Query<User>("select * from users").ToList();
users.Count.IsEqualTo(numberOfEntities);
}
}
public void UpdateList()
{
const int numberOfEntities = 100;
var users = new List<User>();
for (var i = 0; i < numberOfEntities; i++)
users.Add(new User { Name = "User " + i, Age = i });
using (var connection = GetOpenConnection())
{
connection.DeleteAll<User>();
var total = connection.Insert(users);
total.IsEqualTo(numberOfEntities);
users = connection.Query<User>("select * from users").ToList();
users.Count.IsEqualTo(numberOfEntities);
foreach (var user in users)
{
user.Name = user.Name + " updated";
}
connection.Update(users);
var name = connection.Query<User>("select * from users").First().Name;
name.Contains("updated").IsTrue();
}
}
public void DeleteList()
{
const int numberOfEntities = 100;
var users = new List<User>();
for (var i = 0; i < numberOfEntities; i++)
users.Add(new User { Name = "User " + i, Age = i });
using (var connection = GetOpenConnection())
{
connection.DeleteAll<User>();
var total = connection.Insert(users);
total.IsEqualTo(numberOfEntities);
users = connection.Query<User>("select * from users").ToList();
users.Count.IsEqualTo(numberOfEntities);
var usersToDelete = users.Take(10).ToList();
connection.Delete(usersToDelete);
users = connection.Query<User>("select * from users").ToList();
users.Count.IsEqualTo(numberOfEntities - 10);
}
}
public void InsertGetUpdate()
{
using (var connection = GetOpenConnection())
{
connection.DeleteAll<User>();
connection.Get<User>(3).IsNull();
//insert with computed attribute that should be ignored
......@@ -123,9 +195,32 @@ public void InsertGetUpdate()
connection.Query<User>("select * from Users").Count().IsEqualTo(0);
connection.Update(notrackedUser).IsEqualTo(false); //returns false, user not found
}
}
public void GetAll()
{
const int numberOfEntities = 100;
var users = new List<User>();
for (var i = 0; i < numberOfEntities; i++)
users.Add(new User { Name = "User " + i, Age = i });
using (var connection = GetOpenConnection())
{
connection.DeleteAll<User>();
var total = connection.Insert(users);
total.IsEqualTo(numberOfEntities);
users = connection.GetAll<User>().ToList();
users.Count.IsEqualTo(numberOfEntities);
var iusers = connection.GetAll<IUser>().ToList();
iusers.Count.IsEqualTo(numberOfEntities);
}
}
public void Transactions()
{
using (var connection = GetOpenConnection())
......@@ -182,7 +277,7 @@ public void BuilderSelectClause()
{
var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() };
data.Add(nU);
nU.Id = (int)connection.Insert<User>(nU);
nU.Id = (int)connection.Insert(nU);
}
var builder = new SqlBuilder();
......@@ -212,6 +307,7 @@ public void BuilderTemplateWOComposition()
using (var connection = GetOpenConnection())
{
connection.DeleteAll<User>();
connection.Insert(new User { Age = 5, Name = "Testy McTestington" });
if (connection.Query<int>(template.RawSql, template.Parameters).Single() != 1)
......@@ -221,11 +317,12 @@ public void BuilderTemplateWOComposition()
public void InsertFieldWithReservedName()
{
using (var conneciton = GetOpenConnection())
using (var connection = GetOpenConnection())
{
var id = conneciton.Insert(new Result() { Name = "Adam", Order = 1 });
connection.DeleteAll<User>();
var id = connection.Insert(new Result() { Name = "Adam", Order = 1 });
var result = conneciton.Get<Result>(id);
var result = connection.Get<Result>(id);
result.Order.IsEqualTo(1);
}
......
......@@ -59,6 +59,9 @@
<Name>Dapper NET40</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Readme.md" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
......
......@@ -5,58 +5,87 @@ Features
--------
Dapper.Contrib contains a number of helper methods for inserting, getting, updating and deleting files.
The object you are working with must have a property named Id or a property marked with a [Key] attribute. As with dapper,
all extension methods assume the connection is already open, they will fail if the connection is closed.
As with dapper, all extension methods assume the connection is already open, they will fail if the
connection is closed. The full list of extension methods in Dapper.Contrib right now are:
Inserts
-------
```csharp
public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null)
T Get<T>(id);
IEnumerable<T> GetAll<T>();
int Insert<T>(T obj);
int Insert<T>(Enumerable<T> list);
bool Update<T>(T obj);
bool Update<T>(Enumerable<T> list);
bool Delete<T>(T obj);
bool Delete<T>(Enumerable<T> list);
bool DeleteAll<T>();
```
For these extensions to work, the entity in question _MUST_ have a key-property, a property named "id" or decorated with
a [Key] attribute.
```csharp
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
}
connection.Insert(new Car { Name = "Volvo" });
public class User
{
[Key]
int TheId { get; set; }
string Name { get; set; }
int Age { get; set; }
}
```
Gets
-------
```csharp
public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null)
```
Get one specific entity based on id, or a list of all entities in the table.
```csharp
var car = connection.Get<Car>(1);
var cars = connection.GetAll<Car>();
```
Updates
Inserts
-------
Insert one entity or a list of entities.
```csharp
public static bool Update<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null)
connection.Insert(new Car { Name = "Volvo" });
connection.Insert(cars);
```
Updates
-------
Update one specific entity or update a list of entities.
```csharp
connection.Update(new Car() { Id = 1, Name = "Saab" });
connection.Update(cars);
```
Deletes
-------
```csharp
public static bool Delete<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null)
public static bool DeleteAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null)
```
Delete one specific entity, a list of entities, or _ALL_ entities in the table.
```csharp
connection.Delete(new Car() { Id = 1 });
connection.Delete(cars);
connection.DeleteAll<Car>();
```
Attributes
Special Attributes
----------
Dapper.Contrib makes use of some optional attributes:
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
......@@ -109,6 +110,7 @@ private static bool IsWriteable(PropertyInfo pi)
public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
{
......@@ -122,7 +124,6 @@ private static bool IsWriteable(PropertyInfo pi)
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 + " where " + onlyKey.Name + " = @id";
GetQueries[type.TypeHandle] = sql;
......@@ -156,6 +157,58 @@ private static bool IsWriteable(PropertyInfo pi)
}
return obj;
}
/// <summary>
/// Returns a list of entites from table "Ts".
/// Id of T must be marked with [Key] attribute.
/// Entities created from interfaces are tracked/intercepted for changes and used by the Update() extension
/// for optimal performance.
/// </summary>
/// <typeparam name="T">Interface or type to create and populate</typeparam>
/// <param name="connection">Open SqlConnection</param>
/// <returns>Entity of T</returns>
public static IEnumerable<T> GetAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
{
var type = typeof(T);
var cacheType = typeof (List<T>);
string sql;
if (!GetQueries.TryGetValue(cacheType.TypeHandle, out sql))
{
var keys = KeyPropertiesCache(type);
if (keys.Count() > 1)
throw new DataException("Get<T> only supports an entity with a single [Key] property");
if (!keys.Any())
throw new DataException("Get<T> only supports en entity with a [Key] property");
var onlyKey = keys.First();
var name = GetTableName(type);
// TODO: query information schema and only select fields that are both in information schema and underlying class / interface
sql = "select * from " + name ;
GetQueries[cacheType.TypeHandle] = sql;
}
if (!type.IsInterface) return connection.Query<T>(sql, null, transaction, commandTimeout: commandTimeout);
var result = connection.Query(sql);
var list = new List<T>();
foreach (IDictionary<string, object> res in result)
{
var obj = ProxyGenerator.GetInterfaceProxy<T>();
foreach (var property in TypePropertiesCache(type))
{
var val = res[property.Name];
property.SetValue(obj, val, null);
}
((IProxy)obj).IsDirty = false; //reset change tracking and return
list.Add(obj);
}
return list;
}
private static string GetTableName(Type type)
{
string name;
......@@ -176,14 +229,23 @@ private static string GetTableName(Type type)
}
/// <summary>
/// Inserts an entity into table "Ts" and returns identity id.
/// Inserts an entity into table "Ts" and returns identity id or number if inserted rows if inserting a list.
/// </summary>
/// <param name="connection">Open SqlConnection</param>
/// <param name="entityToInsert">Entity to insert</param>
/// <returns>Identity of inserted entity</returns>
/// <param name="entityToInsert">Entity to insert, can be list of entities</param>
/// <returns>Identity of inserted entity, or number of inserted rows if inserting a list</returns>
public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
{
var isList = false;
var type = typeof(T);
if (type.IsArray || type.IsGenericType)
{
isList = true;
type = type.GetGenericArguments()[0];
}
var name = GetTableName(type);
var sbColumnList = new StringBuilder(null);
var allProperties = TypePropertiesCache(type);
......@@ -207,9 +269,17 @@ private static string GetTableName(Type type)
if (i < allPropertiesExceptKeyAndComputed.Count() - 1)
sbParameterList.Append(", ");
}
var adapter = GetFormatter(connection);
return adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(),
sbParameterList.ToString(), keyProperties, entityToInsert);
if (!isList) //single entity
{
var adapter = GetFormatter(connection);
return adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(),
sbParameterList.ToString(), keyProperties, entityToInsert);
}
//insert list of entities
var cmd = String.Format("insert into {0} ({1}) values ({2})", name, sbColumnList, sbParameterList);
return connection.Execute(cmd, entityToInsert, transaction, commandTimeout);
}
/// <summary>
......@@ -229,6 +299,9 @@ private static string GetTableName(Type type)
var type = typeof(T);
if (type.IsArray || type.IsGenericType)
type = type.GetGenericArguments()[0];
var keyProperties = KeyPropertiesCache(type);
if (!keyProperties.Any())
throw new ArgumentException("Entity must have at least one [Key] property");
......@@ -275,6 +348,9 @@ private static string GetTableName(Type type)
var type = typeof(T);
if (type.IsArray || type.IsGenericType)
type = type.GetGenericArguments()[0];
var keyProperties = KeyPropertiesCache(type);
if (!keyProperties.Any())
throw new ArgumentException("Entity must have at least one [Key] property");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册