Skip to content

Commit 4507076

Browse files
committed
Added Get method
1 parent 504a9f5 commit 4507076

20 files changed

Lines changed: 424 additions & 8 deletions

Simple.Data.Ado/AdoAdapter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public IConnectionProvider ConnectionProvider
3737
get { return _connectionProvider; }
3838
}
3939

40+
internal AdoAdapterFinder Finder
41+
{
42+
get { return _finder; }
43+
}
44+
4045
private DatabaseSchema _schema;
4146
private Lazy<AdoAdapterRelatedFinder> _relatedFinder;
4247
private IDbConnection _sharedConnection;
@@ -89,6 +94,11 @@ private AdoAdapterRelatedFinder CreateRelatedFinder()
8994
return new AdoAdapterRelatedFinder(this);
9095
}
9196

97+
public override IDictionary<string, object> Get(string tableName, params object[] keyValues)
98+
{
99+
throw new NotImplementedException();
100+
}
101+
92102
public override IDictionary<string, object> FindOne(string tableName, SimpleExpression criteria)
93103
{
94104
return _finder.FindOne(tableName, criteria);
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Data;
5+
using System.Data.Common;
6+
using System.Dynamic;
7+
using System.Linq;
8+
9+
namespace Simple.Data.Ado
10+
{
11+
class AdoAdapterGetter
12+
{
13+
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, CommandTemplate>> _commandCaches =
14+
new ConcurrentDictionary<string, ConcurrentDictionary<string, CommandTemplate>>();
15+
private readonly AdoAdapter _adapter;
16+
private readonly IDbTransaction _transaction;
17+
private readonly IDbConnection _connection;
18+
19+
public AdoAdapterGetter(AdoAdapter adapter) : this(adapter, null)
20+
{
21+
}
22+
23+
public AdoAdapterGetter(AdoAdapter adapter, IDbTransaction transaction)
24+
{
25+
if (adapter == null) throw new ArgumentNullException("adapter");
26+
_adapter = adapter;
27+
28+
if (transaction != null)
29+
{
30+
_transaction = transaction;
31+
_connection = transaction.Connection;
32+
}
33+
}
34+
35+
public IDictionary<string, object> FindOne(string tableName, SimpleExpression criteria)
36+
{
37+
if (criteria == null) return FindAll(_adapter.GetSchema().BuildObjectName(tableName)).FirstOrDefault();
38+
var commandTemplate = GetCommandTemplate(tableName, criteria);
39+
return ExecuteSingletonQuery(commandTemplate, criteria.GetValues());
40+
}
41+
42+
public Func<object[],IDictionary<string,object>> CreateGetDelegate(string tableName, params object[] keyValues)
43+
{
44+
var primaryKey = _adapter.GetSchema().FindTable(tableName).PrimaryKey;
45+
if (primaryKey == null) throw new InvalidOperationException("Table has no primary key.");
46+
if (primaryKey.Length != keyValues.Length) throw new ArgumentException("Incorrect number of values for key.");
47+
48+
49+
var commandBuilder = new GetHelper(_adapter.GetSchema()) .GetCommand(_adapter.GetSchema().FindTable(tableName), keyValues);
50+
51+
var command = commandBuilder.GetCommand(_adapter.CreateConnection());
52+
command = _adapter.CommandOptimizer.OptimizeFindOne(command);
53+
54+
var commandTemplate =
55+
commandBuilder.GetCommandTemplate(
56+
_adapter.GetSchema().FindTable(_adapter.GetSchema().BuildObjectName(tableName)));
57+
58+
var cloneable = command as ICloneable;
59+
if (cloneable != null)
60+
{
61+
return args => ExecuteSingletonQuery((IDbCommand)cloneable.Clone(), args, commandTemplate.Index);
62+
}
63+
else
64+
{
65+
return args => ExecuteSingletonQuery(commandTemplate, args);
66+
}
67+
}
68+
69+
private IDictionary<string, object> ExecuteSingletonQuery(IDbCommand command, object[] parameterValues, IDictionary<string,int> index)
70+
{
71+
for (int i = 0; i < command.Parameters.Count; i++)
72+
{
73+
((IDbDataParameter) command.Parameters[i]).Value = FixObjectType(parameterValues[i]);
74+
}
75+
command.Connection = _adapter.CreateConnection();
76+
return TryExecuteSingletonQuery(command.Connection, command, index);
77+
}
78+
79+
public IEnumerable<IDictionary<string, object>> Find(string tableName, SimpleExpression criteria)
80+
{
81+
if (criteria == null) return FindAll(_adapter.GetSchema().BuildObjectName(tableName));
82+
var commandTemplate = GetCommandTemplate(tableName, criteria);
83+
return ExecuteQuery(commandTemplate, criteria.GetValues());
84+
}
85+
86+
private CommandTemplate GetCommandTemplate(string tableName, SimpleExpression criteria)
87+
{
88+
var tableCommandCache = _commandCaches.GetOrAdd(tableName,
89+
_ => new ConcurrentDictionary<string, CommandTemplate>());
90+
91+
var hash = new ExpressionHasher().Format(criteria);
92+
return tableCommandCache.GetOrAdd(hash,
93+
_ =>
94+
new FindHelper(_adapter.GetSchema())
95+
.GetFindByCommand(_adapter.GetSchema().BuildObjectName(tableName), criteria)
96+
.GetCommandTemplate(_adapter.GetSchema().FindTable(_adapter.GetSchema().BuildObjectName(tableName))));
97+
}
98+
99+
private IEnumerable<IDictionary<string, object>> FindAll(ObjectName tableName)
100+
{
101+
return ExecuteQuery("select * from " + _adapter.GetSchema().FindTable(tableName).QualifiedName);
102+
}
103+
104+
private IEnumerable<IDictionary<string, object>> ExecuteQuery(CommandTemplate commandTemplate, IEnumerable<object> parameterValues)
105+
{
106+
var connection = _connection ?? _adapter.CreateConnection();
107+
var command = commandTemplate.GetDbCommand(connection, parameterValues);
108+
command.Transaction = _transaction;
109+
return TryExecuteQuery(connection, command, commandTemplate.Index);
110+
}
111+
112+
private IDictionary<string, object> ExecuteSingletonQuery(CommandTemplate commandTemplate, IEnumerable<object> parameterValues)
113+
{
114+
var connection = _connection ?? _adapter.CreateConnection();
115+
var command = commandTemplate.GetDbCommand(connection, parameterValues);
116+
command.Transaction = _transaction;
117+
return TryExecuteSingletonQuery(connection, command, commandTemplate.Index);
118+
}
119+
120+
private IEnumerable<IDictionary<string, object>> ExecuteQuery(string sql, params object[] values)
121+
{
122+
var connection = _connection ?? _adapter.CreateConnection();
123+
var command = new CommandHelper(_adapter).Create(connection, sql, values);
124+
command.Transaction = _transaction;
125+
return TryExecuteQuery(connection, command);
126+
}
127+
128+
private static IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command)
129+
{
130+
try
131+
{
132+
return command.ToEnumerable(connection);
133+
}
134+
catch (DbException ex)
135+
{
136+
throw new AdoAdapterException(ex.Message, command);
137+
}
138+
}
139+
140+
private static IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
141+
{
142+
try
143+
{
144+
return command.ToEnumerable(connection, index);
145+
}
146+
catch (DbException ex)
147+
{
148+
throw new AdoAdapterException(ex.Message, command);
149+
}
150+
}
151+
152+
private static IDictionary<string, object> TryExecuteSingletonQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
153+
{
154+
command.WriteTrace();
155+
using (connection.MaybeDisposable())
156+
using (command)
157+
{
158+
try
159+
{
160+
if (connection.State != ConnectionState.Open)
161+
connection.Open();
162+
using (var reader = command.ExecuteReader())
163+
{
164+
if (reader.Read())
165+
{
166+
return reader.ToDictionary(index);
167+
}
168+
}
169+
}
170+
catch (DbException ex)
171+
{
172+
throw new AdoAdapterException(ex.Message, command);
173+
}
174+
}
175+
return null;
176+
}
177+
178+
private static IDisposable DisposeWrap(IDbConnection connection)
179+
{
180+
if (connection.State == ConnectionState.Open)
181+
{
182+
return ActionDisposable.NoOp;
183+
}
184+
185+
return new ActionDisposable(connection.Dispose);
186+
}
187+
188+
private static object FixObjectType(object value)
189+
{
190+
if (value == null) return DBNull.Value;
191+
if (TypeHelper.IsKnownType(value.GetType())) return value;
192+
var dynamicObject = value as DynamicObject;
193+
if (dynamicObject != null)
194+
{
195+
return dynamicObject.ToString();
196+
}
197+
return value;
198+
}
199+
}
200+
}

Simple.Data.Ado/AdoOptimizingDelegateFactory.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ public class AdoOptimizingDelegateFactory : OptimizingDelegateFactory
1212
{
1313
public override Func<object[], IDictionary<string, object>> CreateFindOneDelegate(Adapter adapter, string tableName, SimpleExpression criteria)
1414
{
15-
return base.CreateFindOneDelegate(adapter, tableName, criteria);
15+
return new AdoAdapterFinder((AdoAdapter)adapter).CreateFindOneDelegate(tableName, criteria);
16+
}
17+
18+
public override Func<object[], IDictionary<string, object>> CreateGetDelegate(Adapter adapter, string tableName, object[] keyValues)
19+
{
20+
return new AdoAdapterGetter((AdoAdapter) adapter).CreateGetDelegate(tableName, keyValues);
1621
}
1722
}
1823
}

Simple.Data.Ado/FindHelper.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,35 @@ private string GetSelectClause(ObjectName tableName)
4141
return string.Format("select {0} from {1}", string.Join(", ", table.Columns.Select(c => c.QualifiedName)), table.QualifiedName);
4242
}
4343
}
44+
45+
internal class GetHelper
46+
{
47+
private readonly DatabaseSchema _schema;
48+
private readonly ICommandBuilder _commandBuilder;
49+
50+
public GetHelper(DatabaseSchema schema)
51+
{
52+
_schema = schema;
53+
_commandBuilder = new CommandBuilder(schema);
54+
}
55+
56+
public ICommandBuilder GetCommand(Table table, params object[] keyValues)
57+
{
58+
_commandBuilder.Append(GetSelectClause(table));
59+
var param = _commandBuilder.AddParameter(keyValues[0], table.FindColumn(table.PrimaryKey[0]));
60+
_commandBuilder.Append(string.Format(" where {0} = {1}", _schema.QuoteObjectName(table.PrimaryKey[0]), param.Name));
61+
for (int i = 1; i < table.PrimaryKey.Length; i++)
62+
{
63+
param = _commandBuilder.AddParameter(keyValues[i], table.FindColumn(table.PrimaryKey[i]));
64+
_commandBuilder.Append(string.Format(" and {0} = {1}", _schema.QuoteObjectName(table.PrimaryKey[i]), param.Name));
65+
}
66+
67+
return _commandBuilder;
68+
}
69+
70+
private string GetSelectClause(Table table)
71+
{
72+
return string.Format("select {0} from {1}", string.Join(", ", table.Columns.Select(c => c.QualifiedName)), table.QualifiedName);
73+
}
74+
}
4475
}

Simple.Data.Ado/Simple.Data.Ado.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<Compile Include="AdoAdapter.cs" />
5050
<Compile Include="AdoAdapterException.cs" />
5151
<Compile Include="AdoAdapterFinder.cs" />
52+
<Compile Include="AdoAdapterGetter.cs" />
5253
<Compile Include="AdoAdapterInserter.cs" />
5354
<Compile Include="AdoAdapterRelatedFinder.cs" />
5455
<Compile Include="AdoAdapterTransaction.cs" />
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using NUnit.Framework;
6+
using Simple.Data.Mocking.Ado;
7+
8+
namespace Simple.Data.IntegrationTest
9+
{
10+
[TestFixture]
11+
public class GetTest : DatabaseIntegrationContext
12+
{
13+
protected override void SetSchema(MockSchemaProvider schemaProvider)
14+
{
15+
schemaProvider.SetTables(new[] { "dbo", "Users", "BASE TABLE" },
16+
new[] { "dbo", "MyTable", "BASE TABLE" });
17+
schemaProvider.SetColumns(new object[] { "dbo", "Users", "Id", true },
18+
new[] { "dbo", "Users", "Name" },
19+
new[] { "dbo", "Users", "Password" },
20+
new[] { "dbo", "Users", "Age" },
21+
new[] { "dbo", "MyTable", "Column1"},
22+
new[] { "dbo", "MyTable", "Column2"});
23+
schemaProvider.SetPrimaryKeys(
24+
new object[] { "dbo", "Users", "Id", 0 },
25+
new object[] { "dbo", "MyTable", "Column1", 0 },
26+
new object[] { "dbo", "MyTable", "Column2", 1 });
27+
}
28+
29+
private const string UsersColumns = "[dbo].[Users].[Id], [dbo].[Users].[Name], [dbo].[Users].[Password], [dbo].[Users].[Age]";
30+
31+
[Test]
32+
public void TestGetWithSingleColumn()
33+
{
34+
_db.Users.Get(1);
35+
GeneratedSqlIs("select " + UsersColumns + " from [dbo].[users] where [id] = @p1");
36+
Parameter(0).Is(1);
37+
}
38+
39+
[Test]
40+
public void TestGetWithTwoColumns()
41+
{
42+
_db.MyTable.Get(1,2);
43+
GeneratedSqlIs("select [dbo].[MyTable].[Column1], [dbo].[MyTable].[Column2] from [dbo].[MyTable] where [Column1] = @p1 and [Column2] = @p2");
44+
Parameter(0).Is(1);
45+
Parameter(1).Is(2);
46+
}
47+
}
48+
}

Simple.Data.BehaviourTest/Simple.Data.BehaviourTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<Compile Include="DeleteTest.cs" />
6262
<Compile Include="ExceptionsTesting.cs" />
6363
<Compile Include="FindTest.cs" />
64+
<Compile Include="GetTest.cs" />
6465
<Compile Include="InsertTest.cs" />
6566
<Compile Include="NaturalNamingTest.cs" />
6667
<Compile Include="ProcedureTest.cs" />

Simple.Data.Mocking/XmlMockAdapter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public XElement Data
2727
get { return _data.Value; }
2828
}
2929

30+
public override IDictionary<string, object> Get(string tableName, params object[] keyValues)
31+
{
32+
throw new NotImplementedException();
33+
}
34+
3035
public override IEnumerable<IDictionary<string, object>> Find(string tableName, SimpleExpression criteria)
3136
{
3237
if (criteria == null) return FindAll(tableName);

Simple.Data.SqlTest/GetTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using NUnit.Framework;
2+
3+
namespace Simple.Data.SqlTest
4+
{
5+
[TestFixture]
6+
public class GetTests
7+
{
8+
[TestFixtureSetUp]
9+
public void Setup()
10+
{
11+
DatabaseHelper.Reset();
12+
}
13+
14+
[Test]
15+
public void TestGet()
16+
{
17+
var db = DatabaseHelper.Open();
18+
var user = db.Users.Get(1);
19+
Assert.AreEqual(1, user.Id);
20+
}
21+
}
22+
}

Simple.Data.SqlTest/Simple.Data.SqlTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
<Compile Include="DbImage.cs" />
6262
<Compile Include="DeleteTest.cs" />
6363
<Compile Include="EnumTest.cs" />
64+
<Compile Include="GetTests.cs" />
6465
<Compile Include="InsertTests.cs" />
6566
<Compile Include="NaturalJoinTest.cs" />
6667
<Compile Include="ProcedureTest.cs" />

0 commit comments

Comments
 (0)