-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate dapper type handlers outside the domain project #737
Comments
I found a workaround and I wonder if this could be a first class citizen in Vogen (or in a separate extension project using System.Data;
using System.Reflection;
using Dapper;
public class StrongIdValueTypeHandler<TValueObject> : SqlMapper.TypeHandler<TValueObject> where TValueObject : struct, IEquatable<int>
{
// ReSharper disable once StaticMemberInGenericType
private static readonly MethodInfo FromMethod;
// ReSharper disable once StaticMemberInGenericType
private static readonly PropertyInfo ValueProperty;
static StrongIdValueTypeHandler()
{
FromMethod = typeof(TValueObject).GetMethod("From", BindingFlags.Public | BindingFlags.Static)
?? throw new InvalidOperationException($"The type {typeof(TValueObject)} must have a static 'From' method.");
ValueProperty = typeof(TValueObject).GetProperty("Value", BindingFlags.Public | BindingFlags.Instance)
?? throw new InvalidOperationException($"The type {typeof(TValueObject)} must have a 'Value' property.");
}
public override void SetValue(IDbDataParameter parameter, TValueObject valueObject)
{
parameter.DbType = DbType.Int32;
parameter.Value = ValueProperty.GetValue(valueObject) ?? DBNull.Value;
}
public override TValueObject Parse(object? value)
{
if (value is null or DBNull)
{
return default;
}
if (value is int intValue)
{
return (TValueObject)(FromMethod.Invoke(null, [intValue])
?? throw new InvalidOperationException($"Method {typeof(TValueObject)}.From(int value) returned null."));
}
throw new InvalidCastException($"Unable to cast value of type {value.GetType()} to {typeof(TValueObject)}");
}
} then, it is applied for all value objects wrapping an var strongIdTypes = assembly.GetTypes()
.Where(
type => type is { IsValueType: true, IsPrimitive: false }
&& typeof(IEquatable<int>).IsAssignableFrom(type)
&& type.GetCustomAttributes<GeneratedCodeAttribute>().Any(attr => attr.Tool == "Vogen"))
.ToArray();
foreach (var strongIdType in strongIdTypes)
{
var handlerType = typeof(StrongIdValueTypeHandler<>).MakeGenericType(strongIdType);
object handler = Activator.CreateInstance(handlerType)
?? throw new InvalidOperationException($"Could not create an instance of {handlerType}");
SqlMapper.AddTypeHandler(strongIdType, (SqlMapper.ITypeHandler) handler);
} This way, no code generation is required, and the overhead is neglectable since the reflection results are cached. What do you think? ChatGPT liked it 😄 |
Thanks for the feedback @marcwittke - I intend to add the ability to generate most of the converters/serialisers into the project where they're declared rather than the project where the value object is declared. Thanks for suggesting your approach. However, I wouldn't want to add any Reflection code as I think it'd break a fair few users who use Vogen in trimmed/AOT apps (I know I'd break my own projects if I did this). It doesn't look to difficult to implement, so I'll attempt it at some point in the new couple of weeks. Thanks again, Steve |
Describe the feature
Referring to the Note in the docs I'd like to request dapper type handlers to be generated outside the project. It was mentioned in #541 but it seems as it wasn't implemented for Dapper.
The text was updated successfully, but these errors were encountered: