Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Added ActiveRecord-style FindBy() method #23

Closed
wants to merge 1 commit into from

2 participants

@JeetKunDoug

Using a DynamicObject-derived class (Finder) and adding FindBy() method to DynamicModel that returns a new Finder. I was originally going to add the dynamic behavior to DynamicModel itself, but then you'd have to cast your instance of DynamicModel to dynamic before calling any FindBy* methods, which I thought wasn't the best option. Thus the separate class that just deals with the dynamic queries.
I have some unit tests of this as well, but didn't quite know what to do with them since Massive is self-contained.

@robconery
Collaborator

Thanks so much for this - I'm not taking commits without tests (or some kind of screenshot of tests) - I suppose I should work that in myself. I do have a console app that I use to make sure nothing breaks - maybe it's time to pop that in.

I like what you've done, unfortunately a fluent-style isn't where I'm wanting to go here. I don't want to abstract the SQL away...

Tests I have (just not on GitHub) but if it's not the kind of thing you're looking to add to Massive, perhaps I'll add it as an extension method from DynamicModel and people could use it if they like the ruby-style thing. If nothing else, it was an interesting intellectual exercise for me, as I was curious what all you could do with DynamicObject's TryInvokeMember.

If you end up wanting to do something like this I can commit the unit tests, but otherwise as I said I'll probably split it out into some MassiveExtensions or something, commit it with tests, and see if anyone else cares :)

@robconery robconery closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 90 additions and 2 deletions.
  1. +79 −2 Massive.cs
  2. +11 −0 README.markdown
View
81 Massive.cs
@@ -7,8 +7,6 @@
using System.Dynamic;
using System.Linq;
using System.Text;
-using System.Collections;
-using System.Text.RegularExpressions;
namespace Massive {
public static class ObjectExtensions {
@@ -362,5 +360,84 @@ public class DynamicModel {
var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns,TableName, PrimaryKeyField);
return Fetch(sql, key).FirstOrDefault();
}
+
+ public dynamic FindBy()
+ {
+ return new Finder(this);
+ }
+ }
+
+ public class Finder: DynamicObject
+ {
+ private readonly DynamicModel _model;
+
+ public Finder(DynamicModel model)
+ {
+ _model = model;
+ }
+
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+ {
+ var argList = new List<object>(args);
+ var sql = buildSql(binder, argList);
+ result = _model.Fetch(sql, argList.ToArray());
+ return true;
+ }
+
+ private string buildSql(InvokeMemberBinder binder, List<object> args)
+ {
+ var fieldNames = binder.Name.Split(new string[] { "And" }, StringSplitOptions.RemoveEmptyEntries);
+ var sql = new StringBuilder();
+ var columns = "*";
+ var orderBy = string.Empty;
+ if (args.Count>fieldNames.Length)
+ {
+ ProcessExtraArgs(binder, args, ref columns, ref orderBy);
+ }
+ sql.AppendFormat("SELECT {0} from {1} WHERE ", columns, _model.TableName);
+ AddWhereClause(fieldNames, sql);
+ AddOrderBy(orderBy, sql);
+ return sql.ToString();
+ }
+
+ private void AddOrderBy(string orderBy, StringBuilder sql)
+ {
+ if (orderBy!=string.Empty)
+ {
+ sql.AppendFormat(" ORDER BY {0}", orderBy);
+ }
+ }
+
+ private void AddWhereClause(string[] fieldNames, StringBuilder sql)
+ {
+ int paramCount = 0;
+ foreach (var name in fieldNames)
+ {
+ sql.AppendFormat("({0} = @{1})", name, paramCount);
+ paramCount++;
+ if (paramCount < fieldNames.Length)
+ {
+ sql.Append(" AND ");
+ }
+ }
+ }
+
+ private void ProcessExtraArgs(InvokeMemberBinder binder, List<object> args, ref string columns, ref string orderBy)
+ {
+ var paramIndex = args.Count - binder.CallInfo.ArgumentNames.Count;
+ foreach (var paramName in binder.CallInfo.ArgumentNames)
+ {
+ switch (paramName)
+ {
+ case "columns":
+ columns = (string) args[paramIndex];
+ break;
+ case "orderBy":
+ orderBy = (string)args[paramIndex];
+ break;
+ }
+ args.RemoveAt(paramIndex);
+ }
+ }
}
}
View
11 README.markdown
@@ -40,6 +40,17 @@ Now you can query thus:
var products = table.All();
//just grab from category 4. This uses named parameters
var productsFour = table.All(columns: "ProductName as Name", where: "WHERE categoryID=@0",args: 4);
+
+You can also use ActiveRecord-style dynamic property queries like this:
+ var table = new Products();
+ // grab all products form category 4 like above, but using dynamic FindBy
+ var productsFour = table.FindBy().CategoryID(4, columns: "ProductName as Name");
+ // Order them by ProductName
+ var productsFour = table.FindBy().CategoryID(4, columns: "ProductName as Name", orderBy:"ProductName DESC");
+ // Find products by CategoryID AND ProductName
+ var productsFourCat = table.FindBy().CategoryIDAndProductName(4, "Cat");
+
+Note that required query parameters must come before named parameters.
You can also run ad-hoc queries as needed:
var result = tbl.Query("SELECT * FROM Categories");
Something went wrong with that request. Please try again.