Commit b2952360 authored by Marc Gravell's avatar Marc Gravell

Merge branch 'Polylytics-arbitrary-multi-maps'

parents f8a189a9 3cd31e42
...@@ -1711,17 +1711,39 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini ...@@ -1711,17 +1711,39 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini
{ {
return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType); return MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, sql, map, param as object, transaction, buffered, splitOn, commandTimeout, commandType);
} }
/// <summary>
/// Perform a multi mapping query with arbitrary input parameters
/// </summary>
/// <typeparam name="TReturn">The return type</typeparam>
/// <param name="cnn"></param>
/// <param name="sql"></param>
/// <param name="types">array of types in the recordset</param>
/// <param name="map"></param>
/// <param name="param"></param>
/// <param name="transaction"></param>
/// <param name="buffered"></param>
/// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
/// <returns></returns>
public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string sql, Type[] types, Func<object[], TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var results = MultiMapImpl<TReturn>(cnn, command, types, map, splitOn, null, null, true);
return buffered ? results.ToList() : results;
}
#endif #endif
partial class DontMap { } partial class DontMap { }
static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>( static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(
this IDbConnection cnn, string sql, Delegate map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType) this IDbConnection cnn, string sql, Delegate map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType)
{ {
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None); var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, command, map, splitOn, null, null); var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, command, map, splitOn, null, null, true);
return buffered ? results.ToList() : results; return buffered ? results.ToList() : results;
} }
static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn, IDataReader reader, Identity identity) static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn, IDataReader reader, Identity identity, bool finalize)
{ {
object param = command.Parameters; object param = command.Parameters;
identity = identity ?? new Identity(command.CommandText, command.CommandType, cnn, typeof(TFirst), param == null ? null : param.GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) }); identity = identity ?? new Identity(command.CommandText, command.CommandType, cnn, typeof(TFirst), param == null ? null : param.GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth), typeof(TSixth), typeof(TSeventh) });
...@@ -1760,7 +1782,82 @@ partial class DontMap { } ...@@ -1760,7 +1782,82 @@ partial class DontMap { }
{ {
yield return mapIt(reader); yield return mapIt(reader);
} }
command.FireOutputCallbacks(); if(finalize)
{
while (reader.NextResult()) { }
command.FireOutputCallbacks();
}
}
}
finally
{
try
{
if (ownedReader != null)
{
ownedReader.Dispose();
}
}
finally
{
if (ownedCommand != null)
{
ownedCommand.Dispose();
}
if (wasClosed) cnn.Close();
}
}
}
static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn, IDataReader reader, Identity identity, bool finalize)
{
if (types.Length < 1)
{
throw new ArgumentException("you must provide at least one type to deserialize");
}
object param = command.Parameters;
identity = identity ?? new Identity(command.CommandText, command.CommandType, cnn, types[0], param == null ? null : param.GetType(), types);
CacheInfo cinfo = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand ownedCommand = null;
IDataReader ownedReader = null;
bool wasClosed = cnn != null && cnn.State == ConnectionState.Closed;
try
{
if (reader == null)
{
ownedCommand = command.SetupCommand(cnn, cinfo.ParamReader);
if (wasClosed) cnn.Open();
ownedReader = ownedCommand.ExecuteReader();
reader = ownedReader;
}
DeserializerState deserializer = default(DeserializerState);
Func<IDataReader, object>[] otherDeserializers = null;
int hash = GetColumnHash(reader);
if ((deserializer = cinfo.Deserializer).Func == null || (otherDeserializers = cinfo.OtherDeserializers) == null || hash != deserializer.Hash)
{
var deserializers = GenerateDeserializers(types, splitOn, reader);
deserializer = cinfo.Deserializer = new DeserializerState(hash, deserializers[0]);
otherDeserializers = cinfo.OtherDeserializers = deserializers.Skip(1).ToArray();
SetQueryCache(identity, cinfo);
}
Func<IDataReader, TReturn> mapIt = GenerateMapper(types.Length, deserializer.Func, otherDeserializers, map);
if (mapIt != null)
{
while (reader.Read())
{
yield return mapIt(reader);
}
if (finalize)
{
while (reader.NextResult()) { }
command.FireOutputCallbacks();
}
} }
} }
finally finally
...@@ -1806,6 +1903,22 @@ partial class DontMap { } ...@@ -1806,6 +1903,22 @@ partial class DontMap { }
} }
} }
private static Func<IDataReader, TReturn> GenerateMapper<TReturn>(int length, Func<IDataReader, object> deserializer, Func<IDataReader, object>[] otherDeserializers, Func<object[], TReturn> map)
{
return r =>
{
var objects = new object[length];
objects[0] = deserializer(r);
for (var i = 1; i < length; ++i)
{
objects[i] = otherDeserializers[i - 1](r);
}
return map(objects);
};
}
private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader) private static Func<IDataReader, object>[] GenerateDeserializers(Type[] types, string splitOn, IDataReader reader)
{ {
var deserializers = new List<Func<IDataReader, object>>(); var deserializers = new List<Func<IDataReader, object>>();
...@@ -3888,7 +4001,7 @@ private IEnumerable<T> ReadImpl<T>(Type type, bool buffered) ...@@ -3888,7 +4001,7 @@ private IEnumerable<T> ReadImpl<T>(Type type, bool buffered)
}, gridIndex); }, gridIndex);
try try
{ {
foreach (var r in SqlMapper.MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(null, default(CommandDefinition), func, splitOn, reader, identity)) foreach (var r in SqlMapper.MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(null, default(CommandDefinition), func, splitOn, reader, identity, false))
{ {
yield return r; yield return r;
} }
......
...@@ -451,7 +451,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -451,7 +451,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
using (var reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false)) using (var reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false))
{ {
if (!command.Buffered) wasClosed = false; // handing back open reader; rely on command-behavior if (!command.Buffered) wasClosed = false; // handing back open reader; rely on command-behavior
var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(null, CommandDefinition.ForCallback(command.Parameters), map, splitOn, reader, identity); var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(null, CommandDefinition.ForCallback(command.Parameters), map, splitOn, reader, identity, true);
return command.Buffered ? results.ToList() : results; return command.Buffered ? results.ToList() : results;
} }
} finally } finally
...@@ -459,8 +459,53 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -459,8 +459,53 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
if (wasClosed) cnn.Close(); if (wasClosed) cnn.Close();
} }
} }
/// <summary>
/// Perform a multi mapping query with arbitrary input parameters
/// </summary>
/// <typeparam name="TReturn">The return type</typeparam>
/// <param name="cnn"></param>
/// <param name="sql"></param>
/// <param name="types">array of types in the recordset</param>
/// <param name="map"></param>
/// <param name="param"></param>
/// <param name="transaction"></param>
/// <param name="buffered"></param>
/// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
/// <returns></returns>
public static Task<IEnumerable<TReturn>> QueryAsync<TReturn>(this IDbConnection cnn, string sql, Type[] types, Func<object[], TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken));
return MultiMapAsync<TReturn>(cnn, command, types, map, splitOn);
}
private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn)
{
if (types.Length < 1)
{
throw new ArgumentException("you must provide at least one type to deserialize");
}
object param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, types[0], param == null ? null : param.GetType(), types);
var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed;
try {
if (wasClosed) await ((DbConnection)cnn).OpenAsync().ConfigureAwait(false);
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader))
using (var reader = await cmd.ExecuteReaderAsync(command.CancellationToken).ConfigureAwait(false)) {
var results = MultiMapImpl<TReturn>(null, default(CommandDefinition), types, map, splitOn, reader, identity, true);
return command.Buffered ? results.ToList() : results;
}
}
finally {
if (wasClosed) cnn.Close();
}
}
private static IEnumerable<T> ExecuteReaderSync<T>(IDataReader reader, Func<IDataReader,object> func, object parameters) private static IEnumerable<T> ExecuteReaderSync<T>(IDataReader reader, Func<IDataReader, object> func, object parameters)
{ {
using (reader) using (reader)
{ {
......
...@@ -46,6 +46,8 @@ private static void RunTests() ...@@ -46,6 +46,8 @@ private static void RunTests()
method.Invoke(tester, null); method.Invoke(tester, null);
Console.WriteLine(" - OK!"); Console.WriteLine(" - OK!");
} }
Console.WriteLine("(end of tests; press any key)");
Console.ReadKey(); Console.ReadKey();
} }
......
...@@ -120,6 +120,24 @@ public void TestMultiMapWithSplitAsync() ...@@ -120,6 +120,24 @@ public void TestMultiMapWithSplitAsync()
} }
} }
public void TestMultiMapArbitraryWithSplitAsync() {
var sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name";
using (var connection = Program.GetOpenConnection()) {
var productQuery = connection.QueryAsync<Product>(sql, new[] { typeof(Product), typeof(Category) }, (objects) => {
var prod = (Product)objects[0];
prod.Category = (Category)objects[1];
return prod;
});
var product = productQuery.Result.First();
// assertions
product.Id.IsEqualTo(1);
product.Name.IsEqualTo("abc");
product.Category.Id.IsEqualTo(2);
product.Category.Name.IsEqualTo("def");
}
}
public void TestMultiMapWithSplitClosedConnAsync() public void TestMultiMapWithSplitClosedConnAsync()
{ {
var sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name"; var sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name";
...@@ -577,5 +595,113 @@ class Address ...@@ -577,5 +595,113 @@ class Address
public string Name { get; set; } public string Name { get; set; }
public int PersonId { get; set; } public int PersonId { get; set; }
} }
class ReviewBoard
{
public int Id { get; set; }
public string Name { get; set; }
public User User1 { get; set; }
public User User2 { get; set; }
public User User3 { get; set; }
public User User4 { get; set; }
public User User5 { get; set; }
public User User6 { get; set; }
public User User7 { get; set; }
public User User8 { get; set; }
public User User9 { get; set; }
}
class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public void TestMultiMapArbitraryMaps()
{
// please excuse the trite example, but it is easier to follow than a more real-world one
var createSql = @"
create table #ReviewBoards (Id int, Name varchar(20), User1Id int, User2Id int, User3Id int, User4Id int, User5Id int, User6Id int, User7Id int, User8Id int, User9Id int)
create table #Users (Id int, Name varchar(20))
insert #Users values(1, 'User 1')
insert #Users values(2, 'User 2')
insert #Users values(3, 'User 3')
insert #Users values(4, 'User 4')
insert #Users values(5, 'User 5')
insert #Users values(6, 'User 6')
insert #Users values(7, 'User 7')
insert #Users values(8, 'User 8')
insert #Users values(9, 'User 9')
insert #ReviewBoards values(1, 'Review Board 1', 1, 2, 3, 4, 5, 6, 7, 8, 9)
";
using (var connection = Program.GetOpenConnection())
{
connection.ExecuteAsync(createSql).Wait();
try
{
var sql = @"
select
rb.Id, rb.Name,
u1.*, u2.*, u3.*, u4.*, u5.*, u6.*, u7.*, u8.*, u9.*
from #ReviewBoards rb
inner join #Users u1 on u1.Id = rb.User1Id
inner join #Users u2 on u2.Id = rb.User2Id
inner join #Users u3 on u3.Id = rb.User3Id
inner join #Users u4 on u4.Id = rb.User4Id
inner join #Users u5 on u5.Id = rb.User5Id
inner join #Users u6 on u6.Id = rb.User6Id
inner join #Users u7 on u7.Id = rb.User7Id
inner join #Users u8 on u8.Id = rb.User8Id
inner join #Users u9 on u9.Id = rb.User9Id
";
var types = new[] { typeof(ReviewBoard), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User) };
Func<object[], ReviewBoard> mapper = (objects) =>
{
var board = (ReviewBoard)objects[0];
board.User1 = (User)objects[1];
board.User2 = (User)objects[2];
board.User3 = (User)objects[3];
board.User4 = (User)objects[4];
board.User5 = (User)objects[5];
board.User6 = (User)objects[6];
board.User7 = (User)objects[7];
board.User8 = (User)objects[8];
board.User9 = (User)objects[9];
return board;
};
var data = connection.QueryAsync<ReviewBoard>(sql, types, mapper).Result.ToList();
var p = data.First();
p.Id.IsEqualTo(1);
p.Name.IsEqualTo("Review Board 1");
p.User1.Id.IsEqualTo(1);
p.User2.Id.IsEqualTo(2);
p.User3.Id.IsEqualTo(3);
p.User4.Id.IsEqualTo(4);
p.User5.Id.IsEqualTo(5);
p.User6.Id.IsEqualTo(6);
p.User7.Id.IsEqualTo(7);
p.User8.Id.IsEqualTo(8);
p.User9.Id.IsEqualTo(9);
p.User1.Name.IsEqualTo("User 1");
p.User2.Name.IsEqualTo("User 2");
p.User3.Name.IsEqualTo("User 3");
p.User4.Name.IsEqualTo("User 4");
p.User5.Name.IsEqualTo("User 5");
p.User6.Name.IsEqualTo("User 6");
p.User7.Name.IsEqualTo("User 7");
p.User8.Name.IsEqualTo("User 8");
p.User9.Name.IsEqualTo("User 9");
}
finally
{
connection.Execute("drop table #Users drop table #ReviewBoards");
}
}
}
} }
} }
\ No newline at end of file
...@@ -47,6 +47,10 @@ ...@@ -47,6 +47,10 @@
<Reference Include="EntityFramework.SqlServer"> <Reference Include="EntityFramework.SqlServer">
<HintPath>..\packages\EntityFramework.6.1.0\lib\net45\EntityFramework.SqlServer.dll</HintPath> <HintPath>..\packages\EntityFramework.6.1.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
</Reference> </Reference>
<Reference Include="FSharp.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FSharp.Core.4.0.0\lib\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="FSharp.PowerPack, Version=2.0.0.0, Culture=neutral, PublicKeyToken=a19089b1c74d0809, processorArchitecture=MSIL"> <Reference Include="FSharp.PowerPack, Version=2.0.0.0, Culture=neutral, PublicKeyToken=a19089b1c74d0809, processorArchitecture=MSIL">
<Private>True</Private> <Private>True</Private>
<HintPath>..\packages\FSPowerPack.Community.2.0.0.0\Lib\Net40\FSharp.PowerPack.dll</HintPath> <HintPath>..\packages\FSPowerPack.Community.2.0.0.0\Lib\Net40\FSharp.PowerPack.dll</HintPath>
......
...@@ -785,7 +785,105 @@ public void TestMultiMap() ...@@ -785,7 +785,105 @@ public void TestMultiMap()
} }
} }
class ReviewBoard
{
public int Id { get; set; }
public string Name { get; set; }
public User User1 { get; set; }
public User User2 { get; set; }
public User User3 { get; set; }
public User User4 { get; set; }
public User User5 { get; set; }
public User User6 { get; set; }
public User User7 { get; set; }
public User User8 { get; set; }
public User User9 { get; set; }
}
public void TestMultiMapArbitraryMaps()
{
// please excuse the trite example, but it is easier to follow than a more real-world one
var createSql = @"
create table #ReviewBoards (Id int, Name varchar(20), User1Id int, User2Id int, User3Id int, User4Id int, User5Id int, User6Id int, User7Id int, User8Id int, User9Id int)
create table #Users (Id int, Name varchar(20))
insert #Users values(1, 'User 1')
insert #Users values(2, 'User 2')
insert #Users values(3, 'User 3')
insert #Users values(4, 'User 4')
insert #Users values(5, 'User 5')
insert #Users values(6, 'User 6')
insert #Users values(7, 'User 7')
insert #Users values(8, 'User 8')
insert #Users values(9, 'User 9')
insert #ReviewBoards values(1, 'Review Board 1', 1, 2, 3, 4, 5, 6, 7, 8, 9)
";
connection.Execute(createSql);
try
{
var sql = @"
select
rb.Id, rb.Name,
u1.*, u2.*, u3.*, u4.*, u5.*, u6.*, u7.*, u8.*, u9.*
from #ReviewBoards rb
inner join #Users u1 on u1.Id = rb.User1Id
inner join #Users u2 on u2.Id = rb.User2Id
inner join #Users u3 on u3.Id = rb.User3Id
inner join #Users u4 on u4.Id = rb.User4Id
inner join #Users u5 on u5.Id = rb.User5Id
inner join #Users u6 on u6.Id = rb.User6Id
inner join #Users u7 on u7.Id = rb.User7Id
inner join #Users u8 on u8.Id = rb.User8Id
inner join #Users u9 on u9.Id = rb.User9Id
";
var types = new[] { typeof(ReviewBoard), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User), typeof(User) };
Func<object[], ReviewBoard> mapper = (objects) =>
{
var board = (ReviewBoard)objects[0];
board.User1 = (User)objects[1];
board.User2 = (User)objects[2];
board.User3 = (User)objects[3];
board.User4 = (User)objects[4];
board.User5 = (User)objects[5];
board.User6 = (User)objects[6];
board.User7 = (User)objects[7];
board.User8 = (User)objects[8];
board.User9 = (User)objects[9];
return board;
};
var data = connection.Query<ReviewBoard>(sql, types, mapper).ToList();
var p = data.First();
p.Id.IsEqualTo(1);
p.Name.IsEqualTo("Review Board 1");
p.User1.Id.IsEqualTo(1);
p.User2.Id.IsEqualTo(2);
p.User3.Id.IsEqualTo(3);
p.User4.Id.IsEqualTo(4);
p.User5.Id.IsEqualTo(5);
p.User6.Id.IsEqualTo(6);
p.User7.Id.IsEqualTo(7);
p.User8.Id.IsEqualTo(8);
p.User9.Id.IsEqualTo(9);
p.User1.Name.IsEqualTo("User 1");
p.User2.Name.IsEqualTo("User 2");
p.User3.Name.IsEqualTo("User 3");
p.User4.Name.IsEqualTo("User 4");
p.User5.Name.IsEqualTo("User 5");
p.User6.Name.IsEqualTo("User 6");
p.User7.Name.IsEqualTo("User 7");
p.User8.Name.IsEqualTo("User 8");
p.User9.Name.IsEqualTo("User 9");
}
finally
{
connection.Execute("drop table #Users drop table #ReviewBoards");
}
}
public void TestMultiMapGridReader() public void TestMultiMapGridReader()
{ {
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="EntityFramework" version="6.1.0" targetFramework="net45" /> <package id="EntityFramework" version="6.1.0" targetFramework="net45" />
<package id="FSharp.Core" version="4.0.0" targetFramework="net45" />
<package id="FSPowerPack.Community" version="2.0.0.0" /> <package id="FSPowerPack.Community" version="2.0.0.0" />
<package id="Microsoft.SqlServer.Types" version="11.0.1" targetFramework="net45" /> <package id="Microsoft.SqlServer.Types" version="11.0.1" targetFramework="net45" />
<package id="Npgsql" version="2.0.11" /> <package id="Npgsql" version="2.0.11" />
......
This diff is collapsed.
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