Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<LangVersion>default</LangVersion>
<TargetFramework>net8.0</TargetFramework>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Activout.DatabaseClient.Dapper/DapperGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task<IEnumerable<object>> QueryAsync(SqlStatement statement)
.ConfigureAwait(false);
}

public async Task<object> QueryFirstOrDefaultAsync(SqlStatement statement)
public async Task<object?> QueryFirstOrDefaultAsync(SqlStatement statement)
{
return await _dbConnection.QueryFirstOrDefaultAsync(statement.EffectiveType, statement.Sql,
GetDynamicParameters(statement))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<LangVersion>default</LangVersion>

<TargetFramework>net8.0</TargetFramework>

<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions Activout.DatabaseClient.Test/DatabaseClientAsyncTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ namespace Activout.DatabaseClient.Test
{
public interface IUserDaoAsync
{
[SqlUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR)")]
[SqlUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR(255))")]
Task CreateTable();

[SqlUpdate("INSERT INTO user(id, name) VALUES (@id, @name)")]
Task InsertNamed([Bind("id")] int id, [Bind] string name);
Task InsertNamed([Bind("id")] int id, [Bind] string? name);

[SqlUpdate("INSERT INTO user(id, name) VALUES (@id, @Name)")]
Task InsertObject([BindProperties] User user);
Expand Down Expand Up @@ -98,7 +98,7 @@ public async Task TestInsertObjectNull()
await _userDao.CreateTable();

// Act + Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _userDao.InsertObject(null));
await Assert.ThrowsAsync<ArgumentNullException>(() => _userDao.InsertObject(null!));
}

[Fact]
Expand Down
8 changes: 4 additions & 4 deletions Activout.DatabaseClient.Test/DatabaseClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ namespace Activout.DatabaseClient.Test
public class User
{
[Bind("id")] public int Id { get; set; }
public string Name { get; set; }
public string? Name { get; set; }
}

public interface IUserDao
{
[SqlUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR)")]
[SqlUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR(255))")]
void CreateTable();

[SqlUpdate("INSERT INTO user(id, name) VALUES (@id, @name)")]
void InsertNamed([Bind] int id, [Bind("name")] string name);
void InsertNamed([Bind] int id, [Bind("name")] string? name);

[SqlUpdate("INSERT INTO user(id, name) VALUES (@id, @Name)")]
void InsertObject([BindProperties] User user);
Expand Down Expand Up @@ -99,7 +99,7 @@ public void TestInsertObjectNull()
_userDao.CreateTable();

// Act
Assert.Throws<ArgumentNullException>(() => _userDao.InsertObject(null));
Assert.Throws<ArgumentNullException>(() => _userDao.InsertObject(null!));

// Assert
}
Expand Down
1 change: 1 addition & 0 deletions Activout.DatabaseClient/Activout.DatabaseClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<LangVersion>default</LangVersion>
<TargetFramework>net8.0</TargetFramework>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Activout.DatabaseClient/Attributes/BindAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Activout.DatabaseClient.Attributes
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public class BindAttribute : Attribute
{
public string ParameterName { get; }
public string? ParameterName { get; }

public BindAttribute()
{
Expand Down
18 changes: 4 additions & 14 deletions Activout.DatabaseClient/IDatabaseGateway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,19 @@

namespace Activout.DatabaseClient
{
public class QueryParameter
{
public QueryParameter(string name, object value)
{
Name = name;
Value = value;
}

public string Name { get; }
public object Value { get; }
}
public record QueryParameter(string Name, object? Value);

public class SqlStatement
{
public string Sql { get; set; }
public required string Sql { get; init; }
public IList<QueryParameter> Parameters { get; } = new List<QueryParameter>();
public Type EffectiveType { get; set; }
public required Type EffectiveType { get; init; }
}

public interface IDatabaseGateway
{
Task<int> ExecuteAsync(SqlStatement statement);
Task<IEnumerable<object>> QueryAsync(SqlStatement statement);
Task<object> QueryFirstOrDefaultAsync(SqlStatement statement);
Task<object?> QueryFirstOrDefaultAsync(SqlStatement statement);
}
}
2 changes: 1 addition & 1 deletion Activout.DatabaseClient/ITaskConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace Activout.DatabaseClient
{
public interface ITaskConverter
{
object ConvertReturnType(Task<object> task);
object? ConvertReturnType(Task<object?> task);
}
}
2 changes: 1 addition & 1 deletion Activout.DatabaseClient/ITaskConverterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace Activout.DatabaseClient
{
public interface ITaskConverterFactory
{
ITaskConverter CreateTaskConverter(Type actualReturnType);
ITaskConverter? CreateTaskConverter(Type actualReturnType);
}
}
53 changes: 22 additions & 31 deletions Activout.DatabaseClient/Implementation/DatabaseClient.cs
Original file line number Diff line number Diff line change
@@ -1,48 +1,39 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using Activout.DatabaseClient.Attributes;

namespace Activout.DatabaseClient.Implementation
{
public class DatabaseClient<T> : DynamicObject where T : class
{
private readonly DatabaseClientContext _context;
private readonly Type _type;
namespace Activout.DatabaseClient.Implementation;

private readonly IDictionary<MethodInfo, MethodHandler> _methodHandlers =
new ConcurrentDictionary<MethodInfo, MethodHandler>();
public class DatabaseClient<T>(DatabaseClientContext context) : DynamicObject
where T : class
{
private readonly Type _type = typeof(T);
private readonly ConcurrentDictionary<MethodInfo, MethodHandler> _methodHandlers = new();

public DatabaseClient(DatabaseClientContext context)
{
_type = typeof(T);
_context = context;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args, out object? result)
{
args ??= [];
var method = _type.GetTypeInfo()
.GetDeclaredMethods(binder.Name)
.Single(mi => mi.GetParameters().Length == args.Length);

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
if (!_methodHandlers.TryGetValue(method, out var methodHandler))
{
var method = _type.GetTypeInfo()
.GetDeclaredMethods(binder.Name)
.Single(mi => mi.GetParameters().Length == args.Length);

if (!_methodHandlers.TryGetValue(method, out var methodHandler))
var sqlAttribute = method.GetCustomAttribute<AbstractSqlAttribute>();
if (sqlAttribute == null)
{
var sqlAttribute = method.GetCustomAttribute<AbstractSqlAttribute>();
if (sqlAttribute == null)
{
result = null;
return false;
}

methodHandler = new MethodHandler(method, sqlAttribute, _context);
_methodHandlers[method] = methodHandler;
result = null;
return false;
}

result = methodHandler.Call(args);
return true;
methodHandler = new MethodHandler(method, sqlAttribute, context);
_methodHandlers[method] = methodHandler;
}

result = methodHandler.Call(args);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class DatabaseClientContext
{
private static readonly ITaskConverterFactory DefaultTaskConverterFactory = new TaskConverter3Factory();

public IDatabaseGateway Gateway { get; set; }
public IDatabaseGateway Gateway { get; set; } = null!;
public ITaskConverterFactory TaskConverterFactory { get; set; } = DefaultTaskConverterFactory;
}
}
40 changes: 22 additions & 18 deletions Activout.DatabaseClient/Implementation/MethodHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class MethodHandler
private readonly bool _isUpdate;
private readonly Type _effectiveType;
private readonly bool _isAsync;
private readonly ITaskConverter _taskConverter;
private readonly ITaskConverter? _taskConverter;

public MethodHandler(MethodInfo method, AbstractSqlAttribute sqlAttribute, DatabaseClientContext context)
{
Expand All @@ -44,11 +44,13 @@ public MethodHandler(MethodInfo method, AbstractSqlAttribute sqlAttribute, Datab
_isAsync = false;
}

_taskConverter = _context.TaskConverterFactory.CreateTaskConverter(resultType);
if (_isAsync)
{
_taskConverter = _context.TaskConverterFactory.CreateTaskConverter(resultType);
}

_isResultEnumerable = resultType.IsGenericType &&
resultType.Namespace == "System.Collections.Generic" &&
resultType.Name == "IEnumerable`1";
_isResultEnumerable = resultType is
{ IsGenericType: true, Namespace: "System.Collections.Generic", Name: "IEnumerable`1" };

if (_isResultEnumerable)
{
Expand All @@ -60,7 +62,7 @@ public MethodHandler(MethodInfo method, AbstractSqlAttribute sqlAttribute, Datab
}
}

public object Call(object[] args)
public object? Call(object?[] args)
{
var statement = new SqlStatement
{
Expand All @@ -86,40 +88,40 @@ public object Call(object[] args)
}
catch (AggregateException e)
{
throw e.InnerException;
throw e.InnerException ?? e;
}
}

private object QueryFirstOrDefault(SqlStatement statement)
private object? QueryFirstOrDefault(SqlStatement statement)
{
var task = _context.Gateway.QueryFirstOrDefaultAsync(statement);
return _isAsync ? _taskConverter.ConvertReturnType(task) : task.Result;
return _isAsync ? _taskConverter!.ConvertReturnType(task) : task.Result;
}

private object Query(SqlStatement statement)
private object? Query(SqlStatement statement)
{
var task = QueryAsync(statement);
return _isAsync ? _taskConverter.ConvertReturnType(task) : task.Result;
return _isAsync ? _taskConverter!.ConvertReturnType(task) : task.Result;
}

private async Task<object> QueryAsync(SqlStatement statement)
private async Task<object?> QueryAsync(SqlStatement statement)
{
return CastEnumerable(await _context.Gateway.QueryAsync(statement).ConfigureAwait(false));
}

private object CastEnumerable(IEnumerable<object> enumerable)
private object? CastEnumerable(IEnumerable<object> enumerable)
{
var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(_effectiveType);
return castMethod.Invoke(null, new object[] { enumerable });
var castMethod = typeof(Enumerable).GetMethod("Cast")!.MakeGenericMethod(_effectiveType);
return castMethod.Invoke(null, [enumerable]);
}

private object Execute(SqlStatement statement)
{
var task = _context.Gateway.ExecuteAsync(statement);
return _isAsync ? task : (object)task.Result;
return _isAsync ? task : task.Result;
}

private void AddSqlStatementParameters(IReadOnlyList<object> args, SqlStatement statement)
private void AddSqlStatementParameters(object?[] args, SqlStatement statement)
{
var parameters = _method.GetParameters();
for (var index = 0; index < parameters.Length; index++)
Expand Down Expand Up @@ -163,7 +165,9 @@ private static IEnumerable<PropertyInfo> GetProperties(ParameterInfo parameter)
private static string GetName(ParameterInfo parameter)
{
var bind = parameter.GetCustomAttribute<BindAttribute>();
return bind?.ParameterName ?? parameter.Name;
return bind?.ParameterName ??
parameter.Name ??
throw new InvalidOperationException("Parameter name is null.");
}

private static string GetName(MemberInfo member)
Expand Down
64 changes: 0 additions & 64 deletions Activout.DatabaseClient/Implementation/TaskConverter.cs

This file was deleted.

Loading