Skip to content

Commit 5754409

Browse files
committed
- fixed property naming
- fixed conversion and equality checks for nullable types
1 parent a1e0bcf commit 5754409

File tree

5 files changed

+64
-14
lines changed

5 files changed

+64
-14
lines changed

DynamicLinqPadPostgreSqlDriver/DynamicPostgreSqlDriver.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.IO;
1111
using LinqToDB.Data;
1212
using System.Data;
13-
using LinqToDB.Mapping;
1413
using DynamicLinqPadPostgreSqlDriver.Extensions;
1514
using DynamicLinqPadPostgreSqlDriver.UI;
1615
using DynamicLinqPadPostgreSqlDriver.Shared.Extensions;
@@ -163,9 +162,8 @@ private static TableData PrepareTableEntity(IConnectionInfo cxInfo, ModuleBuilde
163162
// add the table attribute to the class
164163
typeBuilder.AddTableAttribute(tableName);
165164

166-
var columnAttributeConstructor = typeof(ColumnAttribute).GetConstructor(new[] { typeof(string) });
167-
168165
var query = SqlHelper.LoadSql("QueryColumns.sql");
166+
var propertyAndFieldNames = new HashSet<string>();
169167

170168
var columns = dbConnection.Query(query, new { DatabaseName = databaseName, TableName = tableName });
171169
foreach (var column in columns)
@@ -197,6 +195,7 @@ private static TableData PrepareTableEntity(IConnectionInfo cxInfo, ModuleBuilde
197195
}
198196

199197
text = $"{columnName} ({fieldType.GetTypeName()})";
198+
propertyAndFieldNames.Add(columnName);
200199
}
201200
else
202201
{
@@ -212,7 +211,7 @@ private static TableData PrepareTableEntity(IConnectionInfo cxInfo, ModuleBuilde
212211
tableExplorerItem.Children.Add(explorerItem);
213212
}
214213

215-
return new TableData(tableName, tableExplorerItem, typeBuilder);
214+
return new TableData(tableName, tableExplorerItem, typeBuilder, propertyAndFieldNames);
216215
}
217216

218217
private void BuildAssociations(IDbConnection connection, ICollection<TableData> preparedTables)

DynamicLinqPadPostgreSqlDriver/Entity.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using LinqToDB;
1+
using DynamicLinqPadPostgreSqlDriver.Extensions;
2+
using LinqToDB;
23
using LinqToDB.Mapping;
34
using System;
45
using System.Collections.Generic;
@@ -68,25 +69,36 @@ private QueryData<T> PrepareQuery<T>(string propertyName) where T : class
6869
if (otherKeyField == null)
6970
throw new Exception($"The key field (other) with column name '{association.OtherKey}' does not exist.");
7071

72+
var key = thisKeyField.GetValue(this);
73+
if (key == null)
74+
return null;
75+
7176
// build query expression
7277
var parameter = Expression.Parameter(typeof(T));
7378
var fieldExpression = (Expression) Expression.Field(parameter, otherKeyField);
7479

80+
Expression fieldNotNullExpression = null;
81+
if (otherKeyField.FieldType.IsNullable())
82+
{
83+
// check whether the field is null before a cast or comparioson
84+
fieldNotNullExpression = Expression.NotEqual(fieldExpression, Expression.Constant(null, otherKeyField.FieldType));
85+
}
86+
7587
if (thisKeyField.FieldType != otherKeyField.FieldType)
7688
{
7789
// the types are not matching, so try to convert the expression
7890
fieldExpression = Expression.Convert(fieldExpression, thisKeyField.FieldType);
7991
}
8092

81-
var key = thisKeyField.GetValue(this);
82-
if (key == null)
83-
return null;
93+
var keyExpression = Expression.Constant(key, thisKeyField.FieldType);
94+
var equalExpression = Expression.Equal(fieldExpression, keyExpression);
8495

85-
var keyExpression = Expression.Constant(key);
86-
var equalsExpression = Expression.Equal(fieldExpression, keyExpression);
96+
var notNullAndEqualExpression = fieldNotNullExpression != null
97+
? Expression.AndAlso(fieldNotNullExpression, equalExpression)
98+
: equalExpression;
8799

88100
// create & compile query
89-
return new QueryData<T>(dataContext, table, Expression.Lambda<Func<T, bool>>(equalsExpression, parameter).Compile());
101+
return new QueryData<T>(dataContext, table, Expression.Lambda<Func<T, bool>>(notNullAndEqualExpression, parameter).Compile());
90102
}
91103

92104
private FieldInfo GetFieldByColumnName(Type type, string columnName)

DynamicLinqPadPostgreSqlDriver/Extensions/TableDataExtensions.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ public static string CreateOneToManyAssociation(this TableData table, TableData
1313
var typedEnumerableType = typeof(IEnumerable<>).MakeGenericType(table.TypeBuilder);
1414

1515
// use the table's explorer item text as property name
16-
var propertyName = table.ExplorerItem.Text;
16+
var propertyName = foreignTable.FindFreePropertyName(table.ExplorerItem.Text);
17+
foreignTable.PropertyAndFieldNames.Add(propertyName);
1718

1819
// create a property in the foreign key's target table
1920
var property = foreignTable.TypeBuilder.DefineProperty(propertyName, typedEnumerableType);
@@ -51,7 +52,8 @@ public static string CreateOneToManyAssociation(this TableData table, TableData
5152
public static string CreateManyToOneAssociation(this TableData table, TableData foreignTable, string primaryKeyName, string foreignKeyName, bool backwardReference = false)
5253
{
5354
// use the foreign table's type name as property name
54-
var propertyName = foreignTable.TypeBuilder.Name;
55+
var propertyName = table.FindFreePropertyName(foreignTable.TypeBuilder.Name);
56+
table.PropertyAndFieldNames.Add(propertyName);
5557

5658
// create a property of the foreign table's type in the table entity
5759
var property = table.TypeBuilder.DefineProperty(propertyName, foreignTable.TypeBuilder);
@@ -82,5 +84,19 @@ public static string CreateManyToOneAssociation(this TableData table, TableData
8284

8385
return property.Name;
8486
}
87+
88+
public static string FindFreePropertyName(this TableData tableData, string propertyName)
89+
{
90+
var originalPropertyName = propertyName;
91+
92+
// check for a naming collision
93+
var i = 1;
94+
while (tableData.PropertyAndFieldNames.Contains(propertyName))
95+
{
96+
propertyName = $"{originalPropertyName}{i++}";
97+
}
98+
99+
return propertyName;
100+
}
85101
}
86102
}

DynamicLinqPadPostgreSqlDriver/Extensions/TypeExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,23 @@ public static string GetTypeName(this Type type)
1515

1616
return type.Name;
1717
}
18+
19+
/// <summary>
20+
/// Checks whether the given <see cref="Type"/> is nullable.
21+
///
22+
/// Adopted from http://stackoverflow.com/a/374663
23+
/// </summary>
24+
/// <param name="type">The type which shall be checked.</param>
25+
/// <returns><c>true</c> if the type is nullable, otherwise <c>false</c></returns>
26+
public static bool IsNullable(this Type type)
27+
{
28+
if (!type.IsValueType)
29+
return true; // ref-type
30+
31+
if (Nullable.GetUnderlyingType(type) != null)
32+
return true; // Nullable<T>
33+
34+
return false; // value-type
35+
}
1836
}
1937
}

DynamicLinqPadPostgreSqlDriver/TableData.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Reflection.Emit;
22
using LINQPad.Extensibility.DataContext;
3+
using System.Collections.Generic;
34

45
namespace DynamicLinqPadPostgreSqlDriver
56
{
@@ -11,11 +12,15 @@ internal class TableData
1112

1213
public TypeBuilder TypeBuilder { get; }
1314

14-
public TableData(string name, ExplorerItem explorerItem, TypeBuilder typeBuilder)
15+
public ISet<string> PropertyAndFieldNames { get; }
16+
17+
public TableData(string name, ExplorerItem explorerItem, TypeBuilder typeBuilder, ISet<string> propertyAndFieldNames)
1518
{
1619
Name = name;
1720
ExplorerItem = explorerItem;
1821
TypeBuilder = typeBuilder;
22+
23+
PropertyAndFieldNames = propertyAndFieldNames;
1924
}
2025
}
2126
}

0 commit comments

Comments
 (0)