Skip to content

Commit

Permalink
Merge pull request #431 from mk3008/430-experiment-typesafe-query-bui…
Browse files Browse the repository at this point in the history
…lder-allows-you-to-specify-a-dataset-in-the-select-clause

Improved the Select clause writing experience
  • Loading branch information
mk3008 authored Jun 1, 2024
2 parents 4dc7ffe + b37bef7 commit fa1fdb7
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 33 deletions.
60 changes: 50 additions & 10 deletions src/Carbunql.TypeSafe/FluentSelectQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
using Carbunql.Building;
using Carbunql.Clauses;
using Carbunql.Definitions;
using Carbunql.Extensions;
using Carbunql.Tables;
using Carbunql.TypeSafe.Extensions;
using Carbunql.Values;
using System.Data;
using System.Linq.Expressions;

namespace Carbunql.TypeSafe;
Expand All @@ -17,23 +19,61 @@ public FluentSelectQuery Select<T>(Expression<Func<T>> expression) where T : cla
#if DEBUG
var analyzed = ExpressionReader.Analyze(expression);
#endif

var body = (NewExpression)expression.Body;

var prmManager = new ParameterManager(GetParameters(), AddParameter);

if (body.Members != null)
if (expression.Body is MemberExpression mem)
{
var c = mem.CompileAndInvoke();
if (c is IDataRow dr)
{
foreach (var item in dr.DataSet.Columns)
{
//Do not add duplicate columns
if (SelectClause != null && SelectClause.Where(x => x.Alias.IsEqualNoCase(item)).FirstOrDefault() != null)
{
continue;
}

//add
this.Select(mem.Member.Name, item);
}
return this;
}
throw new InvalidProgramException();

}
else if (expression.Body is NewExpression ne)
{
var cnt = body.Members.Count();
for (var i = 0; i < cnt; i++)
if (ne.Members != null)
{
var alias = body.Members[i].Name;
var value = ToValue(body.Arguments[i], prmManager.AddParaemter);
this.Select(RemoveRootBracketOrDefault(value)).As(alias);
var columns = GetColumnNames();

var cnt = ne.Members.Count();
for (var i = 0; i < cnt; i++)
{
var alias = ne.Members[i].Name;

//Remove duplicate columns before adding
if (SelectClause != null)
{
//remove
var col = SelectClause.Where(x => x.Alias.IsEqualNoCase(alias)).FirstOrDefault();
if (col != null)
{
SelectClause!.Remove(col);
}
}

//add
var value = ToValue(ne.Arguments[i], prmManager.AddParaemter);
this.Select(RemoveRootBracketOrDefault(value)).As(alias);
}
return this;
}

}

return this;
throw new InvalidProgramException();
}

public FluentSelectQuery InnerJoin<T>(Expression<Func<T>> tableExpression, Expression<Func<bool>> conditionExpression) where T : IDataRow
Expand Down
23 changes: 0 additions & 23 deletions src/Carbunql.TypeSafe/Sql.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,29 +95,6 @@ public static class Sql
return DefineDataSet(expression, Materialized.NotMaterialized);
}

public static T DefineSubQuery<T>(SelectQuery query) where T : IDataRow, new()
{
var instance = new T();

//var info = TableInfoFactory.Create(typeof(T));

instance.DataSet = new QueryDataSet(query);
return instance;
}

public static T DefineSubQuery<T>(FluentSelectQuery<T> query) where T : IDataRow, new()
{
var instance = new T();

instance.DataSet = new QueryDataSet(query);
return instance;
}

public static T DefineSubQuery<T>(Func<FluentSelectQuery<T>> builder) where T : IDataRow, new()
{
return DefineSubQuery<T>(builder.Invoke());
}

public static T DefineDataSet<T>() where T : IDataRow, new()
{
var instance = new T();
Expand Down
146 changes: 146 additions & 0 deletions test/Carbunql.TypeSafe.Test/SelectTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using Xunit.Abstractions;

namespace Carbunql.TypeSafe.Test;

public class SelectTest
{
public SelectTest(ITestOutputHelper output)
{
Output = output;
}

private ITestOutputHelper Output { get; }

[Fact]
public void SelectAllDataSet()
{
var od = Sql.DefineDataSet<order_detail>();

var query = Sql.From(() => od)
.Select(() => od);

var actual = query.ToText();
Output.WriteLine(actual);

var expect = @"SELECT
od.order_detail_id,
od.order_id,
od.product_id,
od.quantity,
od.price
FROM
order_detail AS od";

Assert.Equal(expect, actual, true, true, true);
}

[Fact]
public void SelectAllDataSetMultiple()
{
var od = Sql.DefineDataSet<order_detail>();
var p = Sql.DefineDataSet<product>();

var query = Sql.From(() => od)
.InnerJoin(() => p, () => od.product_id == p.product_id)
.Select(() => od)
.Select(() => p);

var actual = query.ToText();
Output.WriteLine(actual);

var expect = @"SELECT
od.order_detail_id,
od.order_id,
od.product_id,
od.quantity,
od.price,
p.name
FROM
order_detail AS od
INNER JOIN product AS p ON od.product_id = p.product_id";

Assert.Equal(expect, actual, true, true, true);
}

[Fact]
public void SelectAllDataSetMultiple_WithAlias()
{
var od = Sql.DefineDataSet<order_detail>();
var p = Sql.DefineDataSet<product>();

var query = Sql.From(() => od)
.InnerJoin(() => p, () => od.product_id == p.product_id)
.Select(() => od)
.Select(() => new
{
product_price = p.price,
product_name = p.name,
});

var actual = query.ToText();
Output.WriteLine(actual);

var expect = @"SELECT
od.order_detail_id,
od.order_id,
od.product_id,
od.quantity,
od.price,
p.price AS product_price,
p.name AS product_name
FROM
order_detail AS od
INNER JOIN product AS p ON od.product_id = p.product_id";

Assert.Equal(expect, actual, true, true, true);
}

public record product : IDataRow
{
public int product_id { get; set; }
public string name { get; set; } = string.Empty;
public decimal price { get; set; }

// interface property
IDataSet IDataRow.DataSet { get; set; } = null!;
}

public record store : IDataRow
{
public int store_id { get; set; }
public string name { get; set; } = string.Empty;
public string location { get; set; } = string.Empty;

// interface property
IDataSet IDataRow.DataSet { get; set; } = null!;
}

public record order : IDataRow
{
public int order_id { get; set; }
public DateTime order_date { get; set; }
public string customer_name { get; set; } = string.Empty;
public int store_id { get; set; }
public IList<order_detail> order_details { get; init; } = new List<order_detail>();

// interface property
IDataSet IDataRow.DataSet { get; set; } = null!;
}

public record order_detail : IDataRow
{
public int order_detail_id { get; set; }
public int order_id { get; set; }
public int product_id { get; set; }
public int quantity { get; set; }
public decimal price { get; set; }

// interface property
IDataSet IDataRow.DataSet { get; set; } = null!;
}

public record order_detail_product : order_detail
{
public string product_name { get; set; } = string.Empty;
}
}

0 comments on commit fa1fdb7

Please sign in to comment.