Permalink
Browse files

Added CommandOptimizer for super-performance awesomeness

  • Loading branch information...
1 parent 49bcf14 commit 19d2602e0580f1901d6a366a82e9395949ad70cd @markrendle committed Nov 23, 2011
@@ -16,9 +16,15 @@ namespace Simple.Data.Ado
[Export("Ado", typeof(Adapter))]
public partial class AdoAdapter : Adapter, IAdapterWithRelation, IAdapterWithTransactions
{
+ private CommandOptimizer _commandOptimizer = new CommandOptimizer();
private readonly AdoAdapterFinder _finder;
private readonly ProviderHelper _providerHelper = new ProviderHelper();
+ public CommandOptimizer CommandOptimizer
+ {
+ get { return _commandOptimizer; }
+ }
+
public ProviderHelper ProviderHelper
{
get { return _providerHelper; }
@@ -44,6 +50,8 @@ internal AdoAdapter(IConnectionProvider connectionProvider) : this()
_connectionProvider = connectionProvider;
_schema = DatabaseSchema.Get(_connectionProvider, _providerHelper);
_relatedFinder = new Lazy<AdoAdapterRelatedFinder>(CreateRelatedFinder);
+ _commandOptimizer = ProviderHelper.GetCustomProvider<CommandOptimizer>(_connectionProvider) ??
+ new CommandOptimizer();
}
protected override void OnSetup()
@@ -71,6 +79,8 @@ protected override void OnSetup()
}
_schema = DatabaseSchema.Get(_connectionProvider, _providerHelper);
_relatedFinder = new Lazy<AdoAdapterRelatedFinder>(CreateRelatedFinder);
+ _commandOptimizer = ProviderHelper.GetCustomProvider<CommandOptimizer>(_connectionProvider) ??
+ new CommandOptimizer();
}
private AdoAdapterRelatedFinder CreateRelatedFinder()
@@ -54,6 +54,7 @@ public AdoAdapterFinder(AdoAdapter adapter, IDbTransaction transaction)
.GetFindByCommand(_adapter.GetSchema().BuildObjectName(tableName), criteria);
var command = commandBuilder.GetCommand(_adapter.CreateConnection());
+ command = _adapter.CommandOptimizer.OptimizeFindOne(command);
var commandTemplate =
commandBuilder.GetCommandTemplate(
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Simple.Data.Ado
+{
+ using System.Data;
+
+ public class CommandOptimizer
+ {
+ public virtual IDbCommand OptimizeFindOne(IDbCommand command)
+ {
+ return command;
+ }
+ }
+}
@@ -89,6 +89,7 @@
<Compile Include="IObservableQueryRunner.cs" />
<Compile Include="IQueryPager.cs" />
<Compile Include="ISchemaGetter.cs" />
+ <Compile Include="ISqlOptimizer.cs" />
<Compile Include="Joiner.cs" />
<Compile Include="JoinType.cs" />
<Compile Include="OptimizedDictionary.cs" />
@@ -15,6 +15,7 @@ public static class TraceHelper
public static void WriteTrace(this IDbCommand command)
{
// if (Trace.Listeners.Count == 0) return;
+ if (Database.TraceLevel < TraceLevel.Info) return;
try
{
var str = new StringBuilder();
@@ -249,6 +249,42 @@ public void SeparateThreadsShouldSeeDifferentMocks()
Assert.AreEqual(2, r2);
}
+ [Test]
+ public void find_all_when_using_Name_property_should_work()
+ {
+ var adapter = new InMemoryAdapter();
+ adapter.ConfigureJoin("Users", "Id", "Categories", "Categories", "UserId", "User");
+ Database.UseMockAdapter(adapter);
+ var db = Database.Open();
+
+ db.Users.Insert(Id: 1, Name: "Marcus");
+ db.Users.Insert(Id: 2, Name: "Per");
+ db.Categories.Insert(Id: 1, UserId: 1, Name: "Category 1");
+ db.Categories.Insert(Id: 2, UserId: 2, Name: "Category 2");
+
+ var categories = db.Users.FindAll(db.User.Categories.Name == "Category 1").ToList();
+ Assert.NotNull(categories);
+ Assert.AreEqual(1, categories.Count); // FAILS - Count == 0
+ }
+
+ [Test]
+ public void find_all_when_using_CategoryName_property_should_work()
+ {
+ var adapter = new InMemoryAdapter();
+ adapter.ConfigureJoin("Users", "Id", "Categories", "Categories", "UserId", "User");
+ Database.UseMockAdapter(adapter);
+ var db = Database.Open();
+
+ db.Users.Insert(Id: 1, UserName: "Marcus");
+ db.Users.Insert(Id: 2, UserName: "Per");
+ db.Categories.Insert(Id: 1, UserId: 1, CategoryName: "Category 1");
+ db.Categories.Insert(Id: 2, UserId: 2, CategoryName: "Category 2");
+
+ var categories = db.Users.FindAll(db.User.Categories.CategoryName == "Category 1").ToList();
+ Assert.NotNull(categories);
+ Assert.AreEqual(1, categories.Count); // Works find - Count == 1
+ }
+
private static int ThreadTestHelper(int userId)
{
var mockAdapter = new InMemoryAdapter();
@@ -53,6 +53,7 @@
</Compile>
<Compile Include="SqlCeDbParameterFactory.cs" />
<Compile Include="SqlColumn.cs" />
+ <Compile Include="SqlCommandOptimizer.cs" />
<Compile Include="SqlConnectionProvider.cs" />
<Compile Include="SqlObservableQueryRunner.cs" />
<Compile Include="SqlQueryPager.cs" />
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Simple.Data.SqlServer
+{
+ using System.ComponentModel.Composition;
+ using System.Text.RegularExpressions;
+ using Ado;
+
+ [Export(typeof(CommandOptimizer))]
+ public class SqlCommandOptimizer : CommandOptimizer
+ {
+ public override System.Data.IDbCommand OptimizeFindOne(System.Data.IDbCommand command)
+ {
+ command.CommandText = Regex.Replace(command.CommandText, "^SELECT ", "SET NOCOUNT ON; SELECT TOP 1 ",
+ RegexOptions.IgnoreCase);
+ return command;
+ }
+ }
+}
@@ -7,19 +7,28 @@
namespace Simple.Data
{
+ using System.Configuration;
+ using System.Diagnostics;
+
/// <summary>
/// The entry class for Simple.Data. Provides static methods for opening databases,
/// and implements runtime dynamic functionality for resolving database-level objects.
/// </summary>
public partial class Database : DataStrategy
{
+ private static readonly SimpleDataConfigurationSection Configuration;
+
private static readonly IDatabaseOpener DatabaseOpener;
private static IPluralizer _pluralizer;
private readonly Adapter _adapter;
static Database()
{
DatabaseOpener = new DatabaseOpener();
+ Configuration =
+ (SimpleDataConfigurationSection) ConfigurationManager.GetSection("simpleData/simpleDataConfiguration")
+ ?? new SimpleDataConfigurationSection();
+ TraceLevel = Configuration.TraceLevel;
}
/// <summary>
@@ -177,5 +186,12 @@ public static void UseMockAdapter(Func<Adapter> mockAdapterCreator)
{
Data.DatabaseOpener.UseMockAdapter(mockAdapterCreator());
}
+
+ private static TraceLevel? _traceLevel;
+ public static TraceLevel? TraceLevel
+ {
+ get { return _traceLevel ?? Configuration.TraceLevel; }
+ set { _traceLevel = value; }
+ }
}
}
@@ -32,7 +32,6 @@ public WhereClauseHandler(WhereClause whereClause)
private Func<IDictionary<string, object>, bool> FunctionExpressionToWhereClause(SimpleExpression arg)
{
- var key = GetKeyFromLeftOperand(arg);
var function = arg.RightOperand as SimpleFunction;
if (ReferenceEquals(function, null)) throw new InvalidOperationException("Expression type of function but no function supplied.");
if (function.Name.Equals("like", StringComparison.OrdinalIgnoreCase))
@@ -95,16 +94,6 @@ public WhereClauseHandler(WhereClause whereClause)
return d => Resolve(d, arg.LeftOperand).Contains(arg.RightOperand);
}
- private static string GetKeyFromLeftOperand(SimpleExpression arg)
- {
- var reference = arg.LeftOperand as ObjectReference;
-
- if (reference.IsNull()) throw new NotSupportedException("Only ObjectReference types are supported.");
-
- var key = reference.GetName();
- return key;
- }
-
private Func<IDictionary<string,object>, bool> Format(SimpleExpression expression)
{
Func<SimpleExpression, Func<IDictionary<string,object>,bool>> formatter;
@@ -133,11 +122,19 @@ private IList<object> Resolve(IDictionary<string, object> dict, object operand,
{
var objectReference = operand as ObjectReference;
if (objectReference.IsNull()) return new object[0];
+
key = key ?? objectReference.GetAliasOrName();
+ var keys = objectReference.GetAllObjectNames();
+
+ if (keys.Length > 2)
+ {
+ return ResolveSubs(dict, objectReference.GetOwner(), key).ToList();
+ }
+
if (dict.ContainsKey(key))
return new[] {dict[key]};
- var subs = ResolveSubs(dict, objectReference.GetOwner(), key).ToList();
- return subs;
+
+ return new object[0];
}
private IEnumerable<object> ResolveSubs(IDictionary<string, object> dict, ObjectReference objectReference, string key)
@@ -42,6 +42,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
+ <Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
@@ -117,6 +118,7 @@
<Compile Include="Range.cs" />
<Compile Include="Range1.cs" />
<Compile Include="SelectClause.cs" />
+ <Compile Include="SimpleDataConfigurationSection.cs" />
<Compile Include="SimpleEmptyExpression.cs" />
<Compile Include="SimpleFunction.cs" />
<Compile Include="SimpleList.cs" />
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Simple.Data
+{
+ using System.Configuration;
+ using System.Diagnostics;
+
+ public class SimpleDataConfigurationSection : ConfigurationSection
+ {
+ [ConfigurationProperty("traceLevel", DefaultValue = TraceLevel.Info, IsRequired = false)]
+ public TraceLevel TraceLevel
+ {
+ get { return (TraceLevel) this["traceLevel"]; }
+ set { this["traceLevel"] = value; }
+ }
+ }
+}

0 comments on commit 19d2602

Please sign in to comment.