Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Reflection;

Expand Down Expand Up @@ -391,7 +392,7 @@ public static List<IdentityTableWithDifferentPrimary> CreateIdentityTableWithDif
{
RowGuid = Guid.NewGuid(),
ColumnBit = true,
ColumnDateTime = EpocDate.AddDays(index),
ColumnDateTime = EpocDate.AddDays(index).ToString(CultureInfo.InvariantCulture),
ColumnDateTime2 = DateTime.UtcNow,
ColumnDecimal = index,
ColumnFloat = index,
Expand All @@ -413,7 +414,7 @@ public static IdentityTableWithDifferentPrimary CreateIdentityTableWithDifferent
{
RowGuid = Guid.NewGuid(),
ColumnBit = true,
ColumnDateTime = EpocDate,
ColumnDateTime = EpocDate.ToString(CultureInfo.InvariantCulture),
ColumnDateTime2 = DateTime.UtcNow,
ColumnDecimal = Convert.ToDecimal(random.Next(int.MinValue, int.MaxValue)),
ColumnFloat = Convert.ToSingle(random.Next(int.MinValue, int.MaxValue)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
using RepoDb.Attributes;
using System;
using System;
using System.Globalization;
using RepoDb.Attributes;
using RepoDb.Entity;

namespace RepoDb.IntegrationTests.Models
{
[Map("[sc].[IdentityTable]")]
public class IdentityTableWithDifferentPrimary
public class IdentityTableWithDifferentPrimary : BaseEntity<IdentityTableWithDifferentPrimary>
{
public IdentityTableWithDifferentPrimary()
{
Map(p => ColumnDateTime).Convert<string>(x => DateTime.Parse(x, CultureInfo.InvariantCulture));
Map(p => ColumnDateTime).Convert<DateTime, string>(x => x.ToString(CultureInfo.InvariantCulture));
}

public long Id { get; set; }
[Primary]
public Guid RowGuid { get; set; }
public bool? ColumnBit { get; set; }
public DateTime? ColumnDateTime { get; set; }
public string ColumnDateTime { get; set; }
public DateTime? ColumnDateTime2 { get; set; }
public decimal? ColumnDecimal { get; set; }
public double? ColumnFloat { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ public static void Initialize()
var connectionStringForMaster = Environment.GetEnvironmentVariable("REPODB_CONSTR_MASTER", EnvironmentVariableTarget.Process);
var connectionString = Environment.GetEnvironmentVariable("REPODB_CONSTR", EnvironmentVariableTarget.Process);

//// Master connection
//ConnectionStringForMaster = (connectionStringForMaster ?? @"Server=(local);Database=master;Integrated Security=False;User Id=michael;Password=Password123;");

//// RepoDb connection
//ConnectionStringForRepoDb = (connectionString ?? @"Server=(local);Database=RepoDb;Integrated Security=False;User Id=michael;Password=Password123;");

// Master connection
ConnectionStringForMaster = (connectionStringForMaster ?? @"Server=(local);Database=master;Integrated Security=False;User Id=michael;Password=Password123;");
ConnectionStringForMaster = (connectionStringForMaster ?? @"Data Source=CHRISWA;Initial Catalog=master;Trusted_Connection=True;");

// RepoDb connection
ConnectionStringForRepoDb = (connectionString ?? @"Server=(local);Database=RepoDb;Integrated Security=False;User Id=michael;Password=Password123;");
ConnectionStringForRepoDb = (connectionString ?? @"Data Source=CHRISWA;Initial Catalog=RepoDb;Trusted_Connection=True;");


// Set the proper values for type mapper
TypeMapper.Add(typeof(DateTime), System.Data.DbType.DateTime2, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using Microsoft.Data.SqlClient;
using System.Linq;
using RepoDb.Entity;

namespace RepoDb.IntegrationTests
{
Expand Down Expand Up @@ -391,15 +392,20 @@ public void TestSqlConnectionCrudConvertionFromStringToSmallMoney()
#region StringToDateClass

[Map("CompleteTable")]
private class StringToDateClass
private class StringToDateClass : BaseEntity<StringToDateClass>
{
[Primary]
public Guid SessionId { get; set; }
public string ColumnDate { get; set; }

public StringToDateClass()
{
Map(p => p.ColumnDate).Convert<DateTime, string>(o => o.ToString("M'/'d'/'yyyy h:mm:ss tt"));
}
}

[TestMethod]
public void TestSqlConnectionCrudConvertionFromStringToDate()
public void TestSqlConnectionCrudConversionFromStringToDate()
{
// Setup
var entity = new StringToDateClass
Expand All @@ -426,15 +432,20 @@ public void TestSqlConnectionCrudConvertionFromStringToDate()
#region StringToDateTimeClass

[Map("CompleteTable")]
private class StringToDateTimeClass
private class StringToDateTimeClass : BaseEntity<StringToDateTimeClass>
{
[Primary]
public Guid SessionId { get; set; }
public string ColumnDateTime { get; set; }

public StringToDateTimeClass()
{
Map(p => p.ColumnDateTime).Convert<DateTime, string>(o => o.ToString("M'/'d'/'yyyy h:mm:ss tt"));
}
}

[TestMethod]
public void TestSqlConnectionCrudConvertionFromStringToDateTime()
public void TestSqlConnectionCrudConversionFromStringToDateTime()
{
// Setup
var entity = new StringToDateTimeClass
Expand All @@ -461,15 +472,20 @@ public void TestSqlConnectionCrudConvertionFromStringToDateTime()
#region StringToDateTime2Class

[Map("CompleteTable")]
private class StringToDateTime2Class
private class StringToDateTime2Class : BaseEntity<StringToDateTime2Class>
{
[Primary]
public Guid SessionId { get; set; }
public string ColumnDateTime2 { get; set; }

public StringToDateTime2Class()
{
Map(p => p.ColumnDateTime2).Convert<DateTime, string>(o => o.ToString("M'/'d'/'yyyy h:mm:ss tt"));
}
}

[TestMethod]
public void TestSqlConnectionCrudConvertionFromStringToDateTime2()
public void TestSqlConnectionCrudConversionFromStringToDateTime2()
{
// Setup
var entity = new StringToDateTime2Class
Expand Down
35 changes: 35 additions & 0 deletions RepoDb.Core/RepoDb/Entity/BaseEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Linq.Expressions;
using RepoDb.Extensions;
using RepoDb.Interfaces;

namespace RepoDb.Entity
{
/// <summary>
///
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public abstract class BaseEntity<TEntity> where TEntity : class
{
/// <summary>
///
/// </summary>
public static readonly IPropertyMap<TEntity> PropertyMap = new EntityPropertyMap<TEntity>();

static BaseEntity()
{
typeof(TEntity).GetProperties().ForEach(x => PropertyMap.Map(x));
}

/// <summary>
/// Map property by expression.
/// </summary>
/// <typeparam name="TProperty">type of the property</typeparam>
/// <param name="expression">mapping expression</param>
/// <returns>mapper</returns>
public IConverter Map<TProperty>(Expression<Func<TEntity, TProperty>> expression)
{
return PropertyMap.Map(expression);
}
}
}
30 changes: 30 additions & 0 deletions RepoDb.Core/RepoDb/Entity/EntityPropertyConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using RepoDb.Interfaces;

namespace RepoDb.Entity
{
internal class EntityPropertyConverter : IConverter
{
/// <summary>
///
/// </summary>
internal object ToProperty { get; private set; }

/// <summary>
///
/// </summary>
internal object ToObject { get; private set; }

public IConverter Convert<TProperty>(Func<TProperty, object> func)
{
ToObject = func;
return this;
}

public IConverter Convert<TFrom, TProperty>(Func<TFrom, TProperty> func)
{
ToProperty = func;
return this;
}
}
}
94 changes: 94 additions & 0 deletions RepoDb.Core/RepoDb/Entity/EntityPropertyMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using RepoDb.Extensions;
using RepoDb.Interfaces;

namespace RepoDb.Entity
{
internal class EntityPropertyMap<TEntity> : IPropertyMap<TEntity> where TEntity : class
{
private readonly IDictionary<string, IConverter> m_propertyMap = new ConcurrentDictionary<string, IConverter>();

/// <summary>
/// Find the sql property from the property info map.
/// </summary>
/// <param name="prop">property info</param>
/// <returns></returns>
public IConverter Find(PropertyInfo prop)
{
return m_propertyMap.TryGetValue(prop.Name, out var value) ? value : default;
}

/// <summary>
/// Map property info.
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
public IConverter Map(PropertyInfo propertyInfo)
{
var mapper = m_propertyMap.GetOrAdd(propertyInfo.Name, new EntityPropertyConverter());
return mapper;
}

/// <summary>
/// Map property by expression.
/// </summary>
/// <typeparam name="TProperty">type of the property</typeparam>
/// <param name="expression">mapping expression</param>
/// <returns>mapper</returns>
public IConverter Map<TProperty>(Expression<Func<TEntity, TProperty>> expression)
{
var info = (PropertyInfo) GetMemberInfo(expression);
return Map(info);
}

/// <summary>
/// Returns the <see cref="T:System.Reflection.MemberInfo"/> for the specified lambda expression.
/// </summary>
/// <param name="lambda">A lambda expression containing a MemberExpression.</param>
/// <returns>A MemberInfo object for the member in the specified lambda expression.</returns>
private static MemberInfo GetMemberInfo(LambdaExpression lambda)
{
Expression expr = lambda;
while (true)
{
switch (expr.NodeType)
{
case ExpressionType.Lambda:
expr = ((LambdaExpression) expr).Body;
break;

case ExpressionType.Convert:
expr = ((UnaryExpression) expr).Operand;
break;

case ExpressionType.MemberAccess:
var memberExpression = (MemberExpression) expr;
var baseMember = memberExpression.Member;

while (memberExpression != null)
{
var type = memberExpression.Type;
if (type.GetMembers().Any(member => member.Name == baseMember.Name))
{
return type.GetMember(baseMember.Name).First();
}

memberExpression = memberExpression.Expression as MemberExpression;
}

// Make sure we get the property from the derived type.
var paramType = lambda.Parameters[0].Type;
return paramType.GetMember(baseMember.Name).First();

default:
return null;
}
}
}
}
}
49 changes: 49 additions & 0 deletions RepoDb.Core/RepoDb/Extensions/DictionaryExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;

namespace RepoDb.Extensions
{
/// <summary>
/// An extension class for <see cref="IEnumerable{T}"/>.
/// </summary>
public static class DictionaryExtension
{
/// <summary>
/// Retrieve or add a value to dictionary
/// </summary>
/// <typeparam name="TK">key type</typeparam>
/// <typeparam name="TV">value type</typeparam>
/// <param name="dictionary">dictionary</param>
/// <param name="key">key value</param>
/// <param name="value">value</param>
/// <returns>value</returns>
public static TV GetOrAdd<TK, TV>(this IDictionary<TK, TV> dictionary, TK key, TV value)
{
if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, value);
}

return dictionary[key];
}

/// <summary>
/// Retrieve or add a value to dictionary via add function.
/// </summary>
/// <typeparam name="TK">key type</typeparam>
/// <typeparam name="TV">value type</typeparam>
/// <param name="dictionary">dictionary</param>
/// <param name="key">key value</param>
/// <param name="addFunc">add function returning the value</param>
/// <returns>value</returns>
public static TV GetOrAdd<TK, TV>(this IDictionary<TK, TV> dictionary, TK key, Func<TV> addFunc)
{
if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, addFunc());
}

return dictionary[key];
}
}
}
Loading