Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 4598ae3

Browse files
authored
Merge pull request #514 from shift-evgeny/SqlExpressionJoinFixes
Select entire table in SqlExpression.Select() using anonymous types
2 parents fb5d619 + 4b18d7d commit 4598ae3

File tree

6 files changed

+309
-17
lines changed

6 files changed

+309
-17
lines changed

src/ServiceStack.OrmLite.PostgreSQL/PostgreSQLDialectProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ public override string GetColumnDefinition(
128128
//Convert xmin into an integer so it can be used in comparisons
129129
public const string RowVersionFieldComparer = "int8in(xidout(xmin))";
130130

131-
public override string GetRowVersionColumnName(FieldDefinition field)
131+
public override SelectListItem GetRowVersionColumnName(FieldDefinition field)
132132
{
133-
return "xmin as " + GetQuotedColumnName(field.FieldName);
133+
return new SelectListExpression(this, "xmin", field.FieldName);
134134
}
135135

136136
public override void AppendFieldCondition(StringBuilder sqlFilter, FieldDefinition fieldDef, IDbCommand cmd)

src/ServiceStack.OrmLite/Expressions/SqlExpression.cs

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,22 +1449,64 @@ protected virtual object VisitNew(NewExpression nex)
14491449
if (isAnonType)
14501450
{
14511451
var exprs = VisitExpressionList(nex.Arguments);
1452+
14521453
var r = StringBuilderCache.Allocate();
1453-
foreach (object e in exprs)
1454+
for (var i = 0; i < exprs.Count; ++i)
14541455
{
1455-
if (r.Length > 0)
1456+
exprs[i] = SetAnonTypePropertyNamesForSelectExpression(exprs[i], nex.Arguments[i], nex.Members[i]);
1457+
1458+
if (i > 0)
14561459
r.Append(",");
14571460

1458-
r.Append(e);
1461+
r.Append(exprs[i]);
14591462
}
14601463
return StringBuilderCache.ReturnAndFree(r);
14611464
}
14621465

14631466
return CachedExpressionCompiler.Evaluate(nex);
14641467
}
14651468

1469+
private object SetAnonTypePropertyNamesForSelectExpression(object expr, Expression arg, MemberInfo member)
1470+
{
1471+
// When selecting a column use the anon type property name, rather than the table property name, as the returned column name
1472+
1473+
MemberExpression propertyExpr;
1474+
if ((propertyExpr = arg as MemberExpression) != null && propertyExpr.Member.Name != member.Name)
1475+
return new SelectListExpression(DialectProvider, expr.ToString(), member.Name);
1476+
1477+
// When selecting an entire table use the anon type property name as a prefix for the returned column name
1478+
// to allow the caller to distinguish properties with the same names from different tables
1479+
1480+
ParameterExpression paramExpr;
1481+
SelectList selectList;
1482+
if ((paramExpr = arg as ParameterExpression) != null && paramExpr.Name != member.Name && (selectList = expr as SelectList) != null)
1483+
{
1484+
foreach (var item in selectList.Items)
1485+
{
1486+
if (!string.IsNullOrEmpty(item.Alias))
1487+
{
1488+
item.Alias = member.Name + item.Alias;
1489+
}
1490+
else
1491+
{
1492+
var columnItem = item as SelectListColumn;
1493+
if (columnItem != null)
1494+
{
1495+
columnItem.Alias = member.Name + columnItem.ColumnName;
1496+
}
1497+
}
1498+
}
1499+
}
1500+
1501+
return expr;
1502+
}
1503+
14661504
protected virtual object VisitParameter(ParameterExpression p)
14671505
{
1506+
var paramModelDef = p.Type.GetModelDefinition();
1507+
if (paramModelDef != null)
1508+
return DialectProvider.GetColumnNames(paramModelDef, true);
1509+
14681510
return p.Name;
14691511
}
14701512

@@ -2061,6 +2103,118 @@ public EnumMemberAccess(string text, Type enumType)
20612103
public Type EnumType { get; private set; }
20622104
}
20632105

2106+
public abstract class SelectListItem
2107+
{
2108+
protected SelectListItem(IOrmLiteDialectProvider dialectProvider, string alias)
2109+
{
2110+
if (dialectProvider == null)
2111+
throw new ArgumentNullException("dialectProvider");
2112+
2113+
DialectProvider = dialectProvider;
2114+
Alias = alias;
2115+
}
2116+
2117+
/// <summary>
2118+
/// Unquoted alias for the column or expression being selected.
2119+
/// </summary>
2120+
public string Alias { get; set; }
2121+
2122+
protected IOrmLiteDialectProvider DialectProvider { get; }
2123+
2124+
public abstract override string ToString();
2125+
}
2126+
2127+
public class SelectListExpression : SelectListItem
2128+
{
2129+
public SelectListExpression(IOrmLiteDialectProvider dialectProvider, string selectExpression, string alias)
2130+
: base(dialectProvider, alias)
2131+
{
2132+
if (string.IsNullOrEmpty(selectExpression))
2133+
throw new ArgumentNullException("selectExpression");
2134+
if (string.IsNullOrEmpty(alias))
2135+
throw new ArgumentNullException("alias");
2136+
2137+
SelectExpression = selectExpression;
2138+
Alias = alias;
2139+
}
2140+
2141+
/// <summary>
2142+
/// The SQL expression being selected, including any necessary quoting.
2143+
/// </summary>
2144+
public string SelectExpression { get; }
2145+
2146+
public override string ToString()
2147+
{
2148+
return SelectExpression + " AS " + DialectProvider.GetQuotedName(Alias); // Alias is required for a non-column expression
2149+
}
2150+
}
2151+
2152+
public class SelectListColumn : SelectListItem
2153+
{
2154+
public SelectListColumn(IOrmLiteDialectProvider dialectProvider, string columnName, string columnAlias = null, string quotedTableAlias = null)
2155+
: base(dialectProvider, columnAlias)
2156+
{
2157+
if (string.IsNullOrEmpty(columnName))
2158+
throw new ArgumentNullException("columnName");
2159+
2160+
ColumnName = columnName;
2161+
QuotedTableAlias = quotedTableAlias;
2162+
}
2163+
2164+
/// <summary>
2165+
/// Unquoted column name being selected.
2166+
/// </summary>
2167+
public string ColumnName { get; }
2168+
/// <summary>
2169+
/// Table name or alias used to prefix the column name, if any. Already quoted.
2170+
/// </summary>
2171+
public string QuotedTableAlias { get; }
2172+
2173+
public override string ToString()
2174+
{
2175+
var text = DialectProvider.GetQuotedColumnName(ColumnName);
2176+
2177+
if (!string.IsNullOrEmpty(QuotedTableAlias))
2178+
text = QuotedTableAlias + "." + text;
2179+
if (!string.IsNullOrEmpty(Alias))
2180+
text += " AS " + DialectProvider.GetQuotedName(Alias);
2181+
2182+
return text;
2183+
}
2184+
}
2185+
2186+
public class SelectList
2187+
{
2188+
public SelectList()
2189+
{
2190+
Items = new List<SelectListItem>();
2191+
}
2192+
2193+
public SelectList(ICollection<SelectListItem> items)
2194+
{
2195+
if (items == null)
2196+
throw new ArgumentNullException("items");
2197+
2198+
Items = new List<SelectListItem>(items);
2199+
}
2200+
2201+
public List<SelectListItem> Items { get; }
2202+
2203+
public override string ToString()
2204+
{
2205+
var sb = StringBuilderCache.Allocate();
2206+
2207+
foreach (var item in Items)
2208+
{
2209+
if (sb.Length > 0)
2210+
sb.Append(", ");
2211+
sb.Append(item);
2212+
}
2213+
2214+
return StringBuilderCache.ReturnAndFree(sb);
2215+
}
2216+
}
2217+
20642218
public class OrmLiteDataParameter : IDbDataParameter
20652219
{
20662220
public DbType DbType { get; set; }

src/ServiceStack.OrmLite/FieldDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public object GetValue(object onInstance)
7474
public string GetQuotedName(IOrmLiteDialectProvider dialectProvider)
7575
{
7676
return IsRowVersion
77-
? dialectProvider.GetRowVersionColumnName(this)
77+
? dialectProvider.GetRowVersionColumnName(this).ToString()
7878
: dialectProvider.GetQuotedColumnName(FieldName);
7979
}
8080

src/ServiceStack.OrmLite/IOrmLiteDialectProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,10 @@ string ToSelectFromProcedureStatement(object fromObjWithProperties,
157157
bool DoesSequenceExist(IDbCommand dbCmd, string sequencName);
158158

159159
ulong FromDbRowVersion(object value);
160-
string GetRowVersionColumnName(FieldDefinition field);
160+
SelectListItem GetRowVersionColumnName(FieldDefinition field);
161161

162162
string GetColumnNames(ModelDefinition modelDef);
163+
SelectList GetColumnNames(ModelDefinition modelDef, bool tableQualified);
163164

164165
SqlExpression<T> SqlExpression<T>();
165166

src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -546,30 +546,44 @@ public virtual string ToSelectStatement(ModelDefinition modelDef,
546546
return StringBuilderCache.ReturnAndFree(sb);
547547
}
548548

549-
public virtual string GetRowVersionColumnName(FieldDefinition field)
549+
public virtual SelectListItem GetRowVersionColumnName(FieldDefinition field)
550550
{
551-
return GetQuotedColumnName(field.FieldName);
551+
return new SelectListColumn(this, field.FieldName);
552552
}
553553

554554
public virtual string GetColumnNames(ModelDefinition modelDef)
555555
{
556-
var sqlColumns = StringBuilderCache.Allocate();
557-
foreach (var field in modelDef.FieldDefinitions)
556+
return GetColumnNames(modelDef, false).ToString();
557+
}
558+
559+
public virtual SelectList GetColumnNames(ModelDefinition modelDef, bool tableQualified)
560+
{
561+
var tablePrefix = "";
562+
if (tableQualified)
563+
{
564+
tablePrefix = GetQuotedTableName(modelDef);
565+
}
566+
567+
var sqlColumns = new SelectListItem[modelDef.FieldDefinitions.Count];
568+
for (var i = 0; i < sqlColumns.Length; ++i)
558569
{
559-
if (sqlColumns.Length > 0)
560-
sqlColumns.Append(", ");
570+
var field = modelDef.FieldDefinitions[i];
561571

562-
if (field.CustomSelect == null)
572+
if (field.CustomSelect != null)
573+
{
574+
sqlColumns[i] = new SelectListExpression(this, field.CustomSelect, field.FieldName);
575+
}
576+
else if (field.IsRowVersion)
563577
{
564-
sqlColumns.Append(field.GetQuotedName(this));
578+
sqlColumns[i] = GetRowVersionColumnName(field);
565579
}
566580
else
567581
{
568-
sqlColumns.Append(field.CustomSelect + " AS " + field.FieldName);
582+
sqlColumns[i] = new SelectListColumn(this, field.FieldName, null, tablePrefix);
569583
}
570584
}
571585

572-
return StringBuilderCache.ReturnAndFree(sqlColumns);
586+
return new SelectList(sqlColumns);
573587
}
574588

575589
public virtual string ToInsertRowStatement(IDbCommand cmd, object objWithProperties, ICollection<string> insertFields = null)

0 commit comments

Comments
 (0)