Skip to content

Commit ef35f73

Browse files
Merge branch 'dev'
2 parents 2f22a7c + 448c6b2 commit ef35f73

11 files changed

Lines changed: 217 additions & 29 deletions

File tree

FluentSql.Tests/SelectStatement/SelectStatementTest.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,26 @@ public void GetSelectQueryWithJoin2()
381381
Xunit.Assert.IsType<Employee>(employeeSet.FirstOrDefault());
382382
}
383383

384+
[Fact]
385+
public void GetSelectQueryWith4Joins()
386+
{
387+
var store = new EntityStore(_dbConnection);
388+
var startingOrderDate = new DateTime(2015, 12, 1);
389+
var selectQuery = store.GetSelectQuery<Employee>()
390+
.JoinOn<Order>((e, o) => e.Id == o.EmployeeId && o.Id >= 1)
391+
.JoinOn<Order, Customer>((o, c) => o.CustomerId == c.Id)
392+
.JoinOn<Order, OrderDetail>((o, od) => o.Id == od.OrderId)
393+
.Where<Order, Customer, OrderDetail, Employee>((o, c, od, e) => o.OrderDate > startingOrderDate
394+
&& c.City == "Gainesville" && e.Id >= 1 && od.Quantity > 1)
395+
.OrderBy(e => e.Username);
396+
397+
var employeeSet = store.ExecuteQuery(selectQuery);
398+
399+
Xunit.Assert.NotNull(employeeSet);
400+
Xunit.Assert.True(employeeSet.Count() >= 2);
401+
Xunit.Assert.IsType<Employee>(employeeSet.FirstOrDefault());
402+
}
403+
384404
[Fact]
385405
public void GetSelectQueryWithJoin3()
386406
{
@@ -416,6 +436,24 @@ public async void GetSelectQueryWithJoin2Async()
416436
Xunit.Assert.IsType<Employee>(employeeSet.FirstOrDefault());
417437
}
418438

439+
[Fact]
440+
public async void GetSelectQueryWithOneWhereTypeReferenceAsync()
441+
{
442+
var store = new EntityStore(_dbConnection);
443+
var startingOrderDate = new DateTime(2015, 12, 1);
444+
var selectQuery = store.GetSelectQuery<Employee>()
445+
.JoinOn<Order>((e, o) => e.Id == o.EmployeeId && o.Id >= 1)
446+
.JoinOn<Order, Customer>((o, c) => o.CustomerId == c.Id)
447+
.Where<Order>(o => o.OrderDate > startingOrderDate)
448+
.OrderBy(e => e.Username);
449+
450+
var employeeSet = await store.ExecuteQueryAsync(selectQuery);
451+
452+
Xunit.Assert.NotNull(employeeSet);
453+
Xunit.Assert.True(employeeSet.Count() >= 2);
454+
Xunit.Assert.IsType<Employee>(employeeSet.FirstOrDefault());
455+
}
456+
419457
[Fact]
420458
public void GetSelectQueryWithLeftJoin()
421459
{
@@ -794,6 +832,25 @@ public void WhereClauseWithGetDatePartDayOfYear()
794832
Xunit.Assert.NotNull(singleEmployee);
795833
}
796834

835+
[Fact]
836+
public void WhereClauseWithGetDatePartDayOfYear1()
837+
{
838+
var store = new EntityStore(_dbConnection);
839+
var order = store.GetSingle<Order>(o => SqlFunctions.GetDayOfYear(o.OrderDate) <= DateTime.Now.DayOfYear);
840+
841+
Xunit.Assert.NotNull(order);
842+
}
843+
844+
[Fact]
845+
public void WhereClauseWithGetDatePartDayOfYear2()
846+
{
847+
var store = new EntityStore(_dbConnection);
848+
var dayOfYear = 17;
849+
var order = store.GetSingle<Order>(o => dayOfYear >= SqlFunctions.GetDayOfYear(o.OrderDate));
850+
851+
Xunit.Assert.NotNull(order);
852+
}
853+
797854
[Fact]
798855
public void WhereClauseWithGetDatePartDay()
799856
{
@@ -810,6 +867,7 @@ public void WhereClauseWithGetDatePartWeek()
810867
var singleEmployee = store.GetSingle<Employee>(e => SqlFunctions.GetWeek(e.Birthdate) >= 5);
811868

812869
Xunit.Assert.NotNull(singleEmployee);
870+
813871
}
814872

815873
[Fact]
@@ -856,6 +914,21 @@ public void WhereClauseWithGetDatePartMillisecond()
856914

857915
Xunit.Assert.NotNull(singleEmployee);
858916
}
917+
918+
[Fact]
919+
public void WhereClauseThrowsException()
920+
{
921+
var store = new EntityStore(_dbConnection);
922+
string NO_DATETIME_SUPPORT = "SqlFunction does not support DateTime functions or variables. It supports DateTime Entity Types";
923+
924+
var singleEmployee = store.GetSingle<Order>(o => SqlFunctions.GetMillisecond(o.OrderDate) >= 0);
925+
926+
var ex = Xunit.Assert.Throws<Exception>(()
927+
=> store.GetSingle<Order>(o => SqlFunctions.GetMillisecond(DateTime.Now) >= 0));
928+
929+
Xunit.Assert.Equal(NO_DATETIME_SUPPORT, ex.Message);
930+
}
931+
859932
public void Dispose()
860933
{
861934
_dbConnection.Close();

FluentSql.Tests/Support/SqlScripst.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ INSERT INTO [Orders] (CustomerId,EmployeeId,OrderDate,RequiredDate,
215215
INSERT INTO [Orders] (CustomerId,EmployeeID,OrderDate,RequiredDate,
216216
ShippedDate,ShipVia,Freight,ShipName,ShipAddress,
217217
ShipCity,ShipRegion,ShipPostalCode,ShipCountry)
218-
VALUES (1,4,'1/18/2016','2/15/2016','2/25/2016',3,3.25,
218+
VALUES (1,4,'1/1/2016','2/15/2016','2/25/2016',3,3.25,
219219
N'Mexico Centre',N'345 Toledo Drive',N'New México',
220220
NULL,N'456878',N'USA');
221221
INSERT INTO [Orders] (CustomerId,EmployeeID,OrderDate,RequiredDate,

FluentSql.Tests/Support/TestConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class TestConstants
2424

2525
#region Where clause test constants
2626
public static string USERNAME = "MCarter";
27+
public static DateTime? DUMMY_DATE = DateTime.Now;
2728
#endregion
2829

2930
public TestConstants() { }

FluentSql/FluentSql.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<DefineConstants>TRACE</DefineConstants>
2929
<ErrorReport>prompt</ErrorReport>
3030
<WarningLevel>4</WarningLevel>
31+
<DocumentationFile>bin\Release\FluentSql.XML</DocumentationFile>
3132
</PropertyGroup>
3233
<ItemGroup>
3334
<Reference Include="Dapper, Version=1.50.2.0, Culture=neutral, processorArchitecture=MSIL">

FluentSql/SqlFunctions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace FluentSql
99
public class SqlFunctions
1010
{
1111
private static string FUNCTION_DIRECT_CALL = "This function can not be called directly.";
12+
1213
#region DateAdd Function
1314
public static DateTime AddYears(DateTime fieldName, int numberOfYears)
1415
{

FluentSql/SqlGenerators/IQuery.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,21 @@ public interface IQuery<L> : IToSql
3333

3434
IQuery<L> Where(Expression<Func<L, bool>> expression);
3535

36+
IQuery<L> Where<T>(Expression<Func<T, bool>> expression) where T : new();
37+
3638
IQuery<L> Where<T1, T2>(Expression<Func<T1, T2, bool>> expression) where T1 : new() where T2 : new();
3739

3840
IQuery<L> Where<T1, T2, T3>(Expression<Func<T1, T2, T3, bool>> expression)
3941
where T1 : new()
4042
where T2 : new()
4143
where T3 : new();
4244

45+
IQuery<L> Where<T1, T2, T3, T4>(Expression<Func<T1, T2, T3, T4, bool>> expression)
46+
where T1 : new()
47+
where T2 : new()
48+
where T3 : new()
49+
where T4 : new();
50+
4351
IQuery<L> GetTopRows(int topNumberOfRows);
4452

4553
IQuery<L> OrderBy(Expression<Func<L, object>> expression);

FluentSql/SqlGenerators/ISqlGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public interface ISqlGenerator
157157
string GetDateAddFunction(string datePart, Type entityType, string fieldName, int number);
158158

159159
/// <summary>
160-
/// Return the Sql function that resolves the DatePart of a date field
160+
/// Returns the Sql function that resolves the DatePart of a date field
161161
/// </summary>
162162
/// <param name="methodName"></param>
163163
/// <param name="entityType"></param>

FluentSql/SqlGenerators/Query.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace FluentSql.SqlGenerators
1313
{
14-
public class Query<TEntity> : IQuery<TEntity>
14+
public class Query<TEntity> : IQuery<TEntity>, IDisposable
1515
{
1616
#region Protected Properties
1717
protected ExpressionHelper Predicate;
@@ -93,6 +93,20 @@ public virtual IQuery<TEntity> Where(Expression<Func<TEntity, bool>> expression)
9393
return this;
9494
}
9595

96+
public IQuery<TEntity> Where<T>(Expression<Func<T, bool>> expression) where T : new()
97+
{
98+
if (expression == null) return this;
99+
100+
Predicate = new ExpressionHelper(expression, ParameterNameGenerator);
101+
102+
if (Parameters.ParameterNames.Any())
103+
Parameters.AddDynamicParams(Predicate.QueryParameters);
104+
else
105+
Parameters = Predicate.QueryParameters;
106+
107+
return this;
108+
}
109+
96110
public IQuery<TEntity> Where<T1, T2>(Expression<Func<T1, T2, bool>> expression) where T1 : new() where T2 : new()
97111
{
98112
if (expression == null) return this;
@@ -124,6 +138,25 @@ public IQuery<TEntity> Where<T1, T2, T3>(Expression<Func<T1, T2, T3, bool>> expr
124138
return this;
125139
}
126140

141+
142+
public IQuery<TEntity> Where<T1, T2, T3, T4>(Expression<Func<T1, T2, T3, T4, bool>> expression)
143+
where T1 : new()
144+
where T2 : new()
145+
where T3 : new()
146+
where T4 : new()
147+
{
148+
if (expression == null) return this;
149+
150+
Predicate = new ExpressionHelper(expression, ParameterNameGenerator);
151+
152+
if (Parameters.ParameterNames.Any())
153+
Parameters.AddDynamicParams(Predicate.QueryParameters);
154+
else
155+
Parameters = Predicate.QueryParameters;
156+
157+
return this;
158+
}
159+
127160
internal IQuery<TEntity> Where(string leftOperand, ExpressionType predicateOperator, string rightOperand, bool isParametized = false, ExpressionType? linkingOperator = null)
128161
{
129162
if (this.PredicateParts == null)

FluentSql/SqlGenerators/SqlServer/SqlServerSqlGenerator.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,27 @@ public string GetDatePartFunction(string methodName, Type entityType, string fie
183183
if (string.IsNullOrEmpty(methodName) || entityType == null || string.IsNullOrEmpty(fieldName))
184184
throw new ArgumentNullException("Arguements can not be null.");
185185

186+
if (entityType == typeof(DateTime) || entityType == typeof(DateTime?))
187+
throw new Exception("SqlServerGenerator: DatePart function does not support DateTime functions for arguments.");
188+
186189
var datePartFunction = "DATEPART({0}, {1})";
187-
var verifiedField = EntityMapper.Entities[entityType].Properties.FirstOrDefault(p => p.Name == fieldName);
190+
var dateField = string.Empty;
188191

189-
if (verifiedField == null)
192+
if (EntityMapper.Entities.Keys.Contains(entityType))
193+
{
194+
var verifiedField = EntityMapper.Entities[entityType].Properties.FirstOrDefault(p => p.Name == fieldName);
195+
196+
if (verifiedField == null)
197+
throw new Exception(string.Format("Could not find field {0} in type {1}", fieldName, entityType));
198+
199+
dateField = FormatFieldforSql(entityType, fieldName);
200+
}
201+
else
190202
throw new Exception(string.Format("Could not find field {0} in type {1}", fieldName, entityType));
191203

192-
var formattedField = FormatFieldforSql(entityType, fieldName);
193204
var datePart = GetDatePartArgument(methodName);
194205

195-
return string.Format(datePartFunction, datePart, formattedField);
206+
return string.Format(datePartFunction, datePart, dateField);
196207
}
197208

198209
public string GetDateAddFunction(string methodName, Type entityType, string fieldName, int number)
@@ -206,6 +217,9 @@ public string GetDateAddFunction(string methodName, Type entityType, string fiel
206217
if (string.IsNullOrEmpty(fieldName))
207218
throw new ArgumentNullException("Field Name (fieldName) can not be null.");
208219

220+
if (entityType == typeof(DateTime) || entityType == typeof(DateTime?))
221+
throw new Exception("SqlServerGenerator: DateAdd function does not support DateTime function for arguments.");
222+
209223
var DateFunction = "DATEADD({0}, {1}, {2})";
210224
var verifiedField = EntityMapper.Entities[entityType].Properties.FirstOrDefault(p => p.Name == fieldName);
211225

@@ -279,6 +293,7 @@ private string GetDatePartArgument(string methodName)
279293
else
280294
throw new NotSupportedException(string.Format("Method Name not supported: {0}", methodName));
281295
}
296+
282297
#endregion
283298
}
284299
}

FluentSql/Support/Helpers/ExpressionHelper.cs

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public ExpressionHelper(Expression predicateExpression, SqlGeneratorHelper param
2626
}
2727
#endregion
2828

29+
#region Constants
30+
private readonly string NO_DATETIME_SUPPORT = "SqlFunction does not support DateTime functions or variables. It supports DateTime Entity Types";
31+
#endregion
32+
2933
#region Private Properties
3034
private static readonly char[] _period = new char[] { '.' };
3135
private IComparer<ExpressionType> _comparer = new OperatorPrecedenceComparer();
@@ -204,10 +208,20 @@ protected override Expression VisitMember(MemberExpression memberExpression)
204208
_predicateString.Push(memberExpression.ToString() + " = 1");
205209
}
206210
else if (member.MemberType == MemberTypes.Property &&
207-
propertyType == typeof(System.DateTime) &&
211+
(propertyType == typeof(System.DateTime) ||
212+
(propertyType == typeof(DateTime?))) &&
208213
memberExpression.Expression == null)
209214
{
210215
AddToPredicate(memberExpression);
216+
return memberExpression;
217+
}
218+
else if (member.MemberType == MemberTypes.Property &&
219+
propertyType == typeof(Int32) &&
220+
memberExpression.Expression != null &&
221+
memberExpression.ToString().StartsWith("DateTime.Now."))
222+
{
223+
AddToPredicate(memberExpression);
224+
return memberExpression;
211225
}
212226
else if (memberExpression.Expression != null &&
213227
memberExpression.Expression.NodeType == ExpressionType.MemberAccess &&
@@ -471,6 +485,10 @@ private MethodCallExpression ParseSqlFunctions(MethodCallExpression methodCall)
471485
throw new Exception(string.Format("Method not implemented: {0}", methodName ?? "Undetermined method name."));
472486

473487
var fieldTypeExpression = methodCall.Arguments[0];
488+
489+
if (fieldTypeExpression.NodeType == ExpressionType.Convert)
490+
throw new Exception(NO_DATETIME_SUPPORT);
491+
474492
var functionArgument = methodCall.Arguments[1];
475493
var memberExpression = (MemberExpression)fieldTypeExpression;
476494

@@ -496,15 +514,15 @@ private MethodCallExpression ParseSqlFunctions(MethodCallExpression methodCall)
496514
if (methodCall.Arguments.Count < 1)
497515
throw new Exception(string.Format("Method not implemented: {0}", methodName ?? "Undetermined method name."));
498516

499-
var fieldTypeExpression = methodCall.Arguments[0];
500-
var memberExpression = (MemberExpression)fieldTypeExpression;
517+
Type operandType = null;
518+
string operand = string.Empty;
501519

502-
if (fieldTypeExpression == null || memberExpression.Expression == null)
503-
throw new ArgumentException(string.Format("Method {0} expects a field type as parameter.", methodName));
520+
GetDateOperands(methodCall, 0, ref operandType, ref operand);
504521

505-
var entityType = memberExpression.Expression.Type;
506-
var propertyName = GetPropertyName(fieldTypeExpression.ToString());
507-
var datePartFuntion = EntityMapper.SqlGenerator.GetDatePartFunction(methodName, entityType, propertyName);
522+
if (operandType == typeof(DateTime) || operandType == typeof(DateTime?))
523+
throw new Exception(NO_DATETIME_SUPPORT);
524+
525+
var datePartFuntion = EntityMapper.SqlGenerator.GetDatePartFunction(methodName, operandType, operand);
508526

509527
_predicateString.Push(datePartFuntion);
510528
}
@@ -514,6 +532,45 @@ private MethodCallExpression ParseSqlFunctions(MethodCallExpression methodCall)
514532
return methodCall;
515533
}
516534

535+
private void GetDateOperands(MethodCallExpression methodCall, int argOrdinalPosition, ref Type operandType, ref string operand)
536+
{
537+
Expression fieldTypeExpression = methodCall.Arguments[argOrdinalPosition];
538+
539+
if (fieldTypeExpression.NodeType == ExpressionType.Convert)
540+
{
541+
var operandValue = GetDateTimeValue(fieldTypeExpression);
542+
543+
operandType = typeof(DateTime?);
544+
operand = operandValue.HasValue ? operandValue.Value.ToString() : "";
545+
546+
}
547+
else if (fieldTypeExpression.NodeType == ExpressionType.MemberAccess &&
548+
((MemberExpression)fieldTypeExpression).Expression != null &&
549+
((MemberExpression)fieldTypeExpression).Expression.NodeType == ExpressionType.Parameter)
550+
{
551+
var memberExp = (MemberExpression)fieldTypeExpression;
552+
553+
operandType = memberExp.Expression == null ? null : memberExp.Expression.Type;
554+
operand = GetPropertyName(memberExp.ToString());
555+
}
556+
else
557+
{
558+
var operandValue = GetValue((MemberExpression)fieldTypeExpression);
559+
560+
operand = operandValue.ToString();
561+
operandType = typeof(DateTime?);
562+
}
563+
}
564+
565+
private DateTime? GetDateTimeValue(Expression dateMethod)
566+
{
567+
if (dateMethod == null) return null;
568+
569+
var lambdaExp = Expression.Lambda(dateMethod).Compile();
570+
var parameterValue = (DateTime?)lambdaExp.DynamicInvoke();
571+
572+
return parameterValue;
573+
}
517574
#endregion
518575

519576
}

0 commit comments

Comments
 (0)