Comparing Enums with custom Fluent Assertions Equivalency Step - c#

I'm trying to write a custom Equivalency Step for Fluent Assertions to compare Enum values on the subject side back to a string on the exception side.
The problem I seem to be facing is that the subject type passed in to the IEquivalencyStep has been converted to a string before my EquivalencyStep is even invoked.
Is there some magic happening within fluent assertions that's trying to convert the enum straight in to a string?
An example of the code is below:
public class SubjectClass
{
public EnumType Prop1 { get; set; }
}
public enum EnumType
{
A,
B,
C
}
public class ExpectionClass
{
public string Prop1 { get; set; }
}
[TestFixture]
public class ComparingEnumWithTests
{
[Test]
public void Test()
{
var expection = new ExpectionClass() {Prop1 = "bbb"};
var subject = new SubjectClass() {Prop1 = EnumType.B};
subject.ShouldBeEquivalentTo(expection, opt => opt.Using(new ComparingEnumWith<EnumType>(new Dictionary<string, EnumType>()
{
{"aaa", EnumType.A },
{"bbb", EnumType.B },
{"ccc", EnumType.C }
})));
}
}
public class ComparingEnumWith<TEnum> : IEquivalencyStep
where TEnum : struct
{
private readonly IReadOnlyDictionary<string, TEnum> _dictionary;
private readonly Type _enumType;
public ComparingEnumWith(IReadOnlyDictionary<string, TEnum> dictionary)
{
_enumType = typeof(TEnum);
if (!_enumType.IsEnum)
{
throw new ArgumentException("TEnum must be an enum");
}
_dictionary = dictionary;
}
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
{
var subjectType = config.GetSubjectType(context);
return subjectType != null && subjectType == _enumType && context.Expectation is string;
}
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
{
var expected = _dictionary[(string)context.Expectation];
return ((TEnum)context.Subject).Equals(expected);
}
}
UPDATE:
From the problem above I've adapted the code to the below incase anyone else is having the same problem.
public class SubjectClass
{
public EnumType Prop1 { get; set; }
}
public enum EnumType
{
A,
B,
C
}
public class ExpectionClass
{
public string Prop1 { get; set; }
}
[TestFixture]
public class ComparingEnumWithTests
{
[Test]
public void Test()
{
var expection = new ExpectionClass() {Prop1 = "bbb"};
var subject = new SubjectClass() {Prop1 = EnumType.B};
AssertionOptions.EquivalencySteps.Insert<ComparingEnumWith<EnumTypeDataMapProvider, EnumType>>();
subject.ShouldBeEquivalentTo(expection);
}
}
public interface IEnumDataMapProvider<TEnum>
{
IReadOnlyDictionary<string, TEnum> Map { get; }
}
public class EnumTypeDataMapProvider : IEnumDataMapProvider<EnumType>
{
public IReadOnlyDictionary<string, EnumType> Map => new Dictionary<string, EnumType>()
{
{"aaa", EnumType.A},
{"bbb", EnumType.B},
{"ccc", EnumType.C}
};
}
public class ComparingEnumWith<TMapProvider, TEnum> : IEquivalencyStep
where TMapProvider : IEnumDataMapProvider<TEnum>
where TEnum : struct
{
private readonly IReadOnlyDictionary<string, TEnum> _dictionary;
private readonly Type _enumType;
public ComparingEnumWith()
{
_enumType = typeof(TEnum);
if (!_enumType.IsEnum)
{
throw new ArgumentException("TEnum must be an enum");
}
var provider = Activator.CreateInstance<TMapProvider>();
_dictionary = provider.Map;
}
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
{
var subjectType = config.GetSubjectType(context);
return subjectType != null && subjectType == _enumType && context.Expectation is string;
}
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
{
var expected = _dictionary[(string)context.Expectation];
return ((TEnum)context.Subject).Equals(expected);
}
}

That's a bug. You're registering something we call a user equivalency step. But these run after the built-in TryConversionEquivalencyStep and ReferenceEqualityEquivalencyStep. If you can please file this as a bug, we can look at it. As a workaround, consider inserting your custom step before all the built-in steps using AssertionOptions.EquivalencySteps.Insert<ComparingEnumWith<T>>();

Related

How do I set return type from method with an Expression<Func<TModel, TProperty>>

I am wanting to call a method with the signiture something like
string fullname = UserBuilder.DefaultProperty(e => e.Fullname)
I have created a builder class and want to return the default property from a default supplied
public class BuilderGeneric<TModel>
{
private readonly TModel _defaultModel;
public BuilderGeneric(TModel defaultModel)
{
_defaultModel = defaultModel;
}
public object DefaultProperty<TProperty>([NotNull] Expression<Func<TModel, TProperty>> propertyExpression)
{
// Reflection here to get the property
// ...
var propertyType = myPropInfo.PropertyType;
var propertyValue = myPropInfo.GetValue(_defaultModel);
return propertyValue;
}
}
How can I make it so that I dont return an object back but instead the type of the property in the e => e.Fullname) in that example it would be a string
Thanks #canton7
I was trying to create a generic builder class which I have included below along with a unit test.
I hope this helps anyone trying to to a similar thing.
public class BuilderGeneric<TModel>
where TModel : class
{
private readonly TModel _defaultModel;
private TModel BuilderEntity { get; }
public BuilderGeneric(TModel defaultModel)
{
_defaultModel = defaultModel;
BuilderEntity = (TModel)Activator.CreateInstance(typeof(TModel));
}
public TProperty DefaultProperty<TProperty>(Func<TModel, TProperty> property)
{
return property(_defaultModel);
}
public BuilderGeneric<TModel> With<TProperty>(Expression<Func<TModel, TProperty>> property, TProperty value)
{
var memberExpression = (MemberExpression)property.Body;
var propertyInfo = (PropertyInfo)memberExpression.Member;
propertyInfo.SetValue(BuilderEntity, value);
return this;
}
public TModel Build()
{
return BuilderEntity;
}
}
public class UserBuilder : BuilderGeneric<UserProfile>
{
public UserBuilder()
: base(new UserProfile()
{
FullName = "Ian Bowyer",
EmailAddress = "Email#Email.com",
MyGuid = new Guid("12345678-1234-1234-1234-123456789012")
})
{
}
}
public class UserProfile
{
public string FullName { get; set; }
public string EmailAddress { get; set; }
public Guid MyGuid { get; set; }
}
public class TestGenericBuilder
{
[Test]
public void TestTheDefaultPropertyReturns()
{
// Assert
var userBuilder = new UserBuilder()
.With(e => e.FullName, "Bobby Tables")
.With(e => e.MyGuid, new Guid("85EC85CF-2892-40B8-BF63-0C621F78BE27"));
var user = userBuilder.Build();
// Act
var defaultFullname = userBuilder.DefaultProperty(e => e.FullName);
// Assert
defaultFullname.Should().Be("Ian Bowyer");
user.FullName.Should().Be("Bobby Tables");
}
}

C# LINQ, dynamic grouping by [Key] attributes

Consider the following classes:
public class Potato
{
[Key]
public string Farm { get; set; }
[Key]
public int Size { get; set; }
public string Trademark { get; set; }
}
public class Haybell
{
[Key]
public string color { get; set; }
public int StrawCount { get; set; }
}
public class Frog
{
[Key]
public bool IsAlive { get; set; }
[Key]
public bool IsVirulent { get; set; }
public byte LimbCount { get; set; } = 4;
public ConsoleColor Color { get; set; }
}
Each class has properties with [Key] attribute. Is it possible to dynamically group an IEnumerable of any of these classes by their respective [Key] attributes?
I would go for adding extension methods for each your types, like
Option 1:
static class Extensions
{
public static IEnumerable<IGrouping<Tuple<string, int>, Potato>>
GroupByPrimaryKey(this IEnumerable<Potato> e)
{
return e.GroupBy(p => Tuple.Create(p.Farm, p.Size));
}
public static IEnumerable<IGrouping<Tuple<bool, bool>, Frog>>
GroupByPrimaryKey(this IEnumerable<Frog> e)
{
return e.GroupBy(p => Tuple.Create(p.IsAlive, p.IsVirulent));
}
}
If there are lots of types, you may generate the code using t4.
Usage: .GroupByPrimaryKey().
Option 2:
A simpler variation:
static class Extensions
{
public static Tuple<string, int> GetPrimaryKey(this Potato p)
{
return Tuple.Create(p.Farm, p.Size);
}
public static Tuple<bool, bool> GetPrimaryKey(this Frog p)
{
return Tuple.Create(p.IsAlive, p.IsVirulent);
}
}
Usage: .GroupBy(p => p.GetPrimaryKey()).
Option 3:
A solution with reflection is possible, but will be slow. Sketch (far from production-ready!)
class CombinedKey : IEquatable<CombinedKey>
{
object[] _keys;
CombinedKey(object[] keys)
{
_keys = keys;
}
public bool Equals(CombinedKey other)
{
return _keys.SequenceEqual(other._keys);
}
public override bool Equals(object obj)
{
return obj is CombinedKey && Equals((CombinedKey)obj);
}
public override int GetHashCode()
{
return 0;
}
public static CombinedKey GetKey<T>(T instance)
{
return new CombinedKey(GetKeyAttributes(typeof(T)).Select(p => p.GetValue(instance, null)).ToArray());
}
private static PropertyInfo[] GetKeyAttributes(Type type)
{
// you definitely want to cache this
return type.GetProperties()
.Where(p => Attribute.GetCustomAttribute(p, typeof(KeyAttribute)) != null)
.ToArray();
}
}
Usage: GroupBy(p => CombinedKey.GetKey(p))
The challenge here is that you need to build an anonymous type in order to have a GroupBy Expression that can translate to SQL or any other LINQ provider.
I'm not sure that you can do that using reflection (not without some really complex code to create an anonymous type at runtime). But you could create the grouping expression if you were willing to provide an example of the anonymous type as the seed.
public static Expression<Func<TSource, TAnon>> GetAnonymous<TSource,TAnon>(TSource dummy, TAnon example)
{
var ctor = typeof(TAnon).GetConstructors().First();
var paramExpr = Expression.Parameter(typeof(TSource));
return Expression.Lambda<Func<TSource, TAnon>>
(
Expression.New
(
ctor,
ctor.GetParameters().Select
(
(x, i) => Expression.Convert
(
Expression.Property(paramExpr, x.Name), // fetch same named property
x.ParameterType
)
)
), paramExpr);
}
And here's how you would use it (Note: the dummy anonymous type passed to the method is there in order to make the anonymous type a compile-time Type, the method doesn't care what the values are that you pass in for it.) :
static void Main()
{
var groupByExpression = GetAnonymous(new Frog(), new {IsAlive = true, IsVirulent = true});
Console.WriteLine(groupByExpression);
var frogs = new []{ new Frog{ IsAlive = true, IsVirulent = false}, new Frog{ IsAlive = false, IsVirulent = true}, new Frog{ IsAlive = true, IsVirulent = true}};
var grouped = frogs.AsQueryable().GroupBy(groupByExpression);
foreach (var group in grouped)
{
Console.WriteLine(group.Key);
}
}
Which produces:
Param_0 => new <>f__AnonymousType0`2(Convert(Param_0.IsAlive, Boolean), Convert(Param_0.IsVirulent, Boolean))
{ IsAlive = True, IsVirulent = False }
{ IsAlive = False, IsVirulent = True }
{ IsAlive = True, IsVirulent = True }
Somebody had posted a valid answer and removed it later for some reason. Here it is:
Combined key class:
class CombinedKey<T> : IEquatable<CombinedKey<T>>
{
readonly object[] _keys;
public bool Equals(CombinedKey<T> other)
{
return _keys.SequenceEqual(other._keys);
}
public override bool Equals(object obj)
{
return obj is CombinedKey<T> key && Equals(key);
}
public override int GetHashCode()
{
int hash = _keys.Length;
foreach (object o in _keys)
{
if (o != null)
{
hash = hash * 13 + o.GetHashCode();
}
}
return hash;
}
readonly Lazy<Func<T, object[]>> lambdaFunc = new Lazy<Func<T, object[]>>(() =>
{
Type type = typeof(T);
var paramExpr = Expression.Parameter(type);
var arrayExpr = Expression.NewArrayInit(
typeof(object),
type.GetProperties()
.Where(p => (Attribute.GetCustomAttribute(p, typeof(KeyAttribute)) != null))
.Select(p => Expression.Convert(Expression.Property(paramExpr, p), typeof(object)))
.ToArray()
);
return Expression.Lambda<Func<T, object[]>>(arrayExpr, paramExpr).Compile();
}, System.Threading.LazyThreadSafetyMode.PublicationOnly);
public CombinedKey(T instance)
{
_keys = lambdaFunc.Value(instance);
}
}
Caller function and the actual usage:
public static class MyClassWithLogic
{
//Caller to CombinedKey class
private static CombinedKey<Q> NewCombinedKey<Q>(Q instance)
{
return new CombinedKey<Q>(instance);
}
//Extension method for IEnumerables
public static IEnumerable<T> DistinctByPrimaryKey<T>(this IEnumerable<T> entries) where T : class
{
return entries.AsQueryable().GroupBy(NewCombinedKey)
.Select(r => r.First());
}
}
Yes, it is relatively slow, so if it is a problem, then Klaus Gütter's solutions are the way to go.

How to get a specific constructor that matches an Interface?

So playing with my own test Dependency Injector class. (yeah tons out there but this is just for fun)
Works decent but I don't know how to get the correct constructor based on the Interface passed in.
internal class DiContainer
{
private readonly Dictionary<Type, RegistryRecord> registry = new Dictionary<Type, RegistryRecord>();
private static DiContainer instance;
private DiContainer()
{
}
public static DiContainer GetInstance()
{
return instance ??= new DiContainer();
}
public void Register<T, C>() where C : class, T
{
registry.Add(typeof(T), new RegistryRecord
{
InterfaceType = typeof(T),
ConcreteType = typeof(C),
IsSingleTon = false
});
}
public void Register<C>() where C : class
{
Register(typeof(C));
}
public void Register(Type t)
{
registry.Add(t, new RegistryRecord
{
InterfaceType = t,
ConcreteType = t,
IsSingleTon = false
});
}
public void RegisterSingleton<T, C>(C instance = null) where C : class, T
{
registry.Add(typeof(T), new RegistryRecord
{
InterfaceType = typeof(T),
ConcreteType = typeof(C),
IsSingleTon = true,
Instance = instance
});
}
public T Get<T>()
{
return (T) Get(typeof(T));
}
public object Get(Type t)
{
ConstructorInfo constructor;
RegistryRecord r = null;
if (t.IsInterface && registry.ContainsKey(t))
{
r = registry[t];
if (r.IsSingleTon && r.Instance != null) return r.Instance;
constructor = r.ConcreteType.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
}
else
{
//todo how do we select the correct constructor?
constructor = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
}
var parameters = constructor.GetParameters();
//recurse to build dependency chain
var objects = parameters.Select(parameter => Get(parameter.ParameterType)).ToList();
var obj = constructor.Invoke(objects.ToArray());
if (r != null && r.IsSingleTon)
{
r.Instance = obj;
}
return obj;
}
}
internal class RegistryRecord
{
public Type InterfaceType { get; set; }
public Type ConcreteType { get; set; }
public object Instance { get; set; }
public bool IsSingleTon { get; set; }
}
So the problem is
constructor = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
I am just assuming the first constructor which is awful. But I have the definition of the interface I could be using.
How do I get the parameters of my interface and check them against the constructor?
Would like to select a constructor that matches the interface, or at least partially matches optimally.
edit
An example
using System;
namespace DITest
{
internal class Program
{
private static void Main(string[] args)
{
var di = DiContainer.GetInstance();
//Register classes / interfaces
di.Register<IPhoneResolver, PhoneResolver>();
di.Register<Customer>();
//Get class where dependency should be injected
var x = di.Get<Customer>();
Console.WriteLine(x.resolver.Name);
Console.Read();
}
}
public class Customer
{
//Remove this and everything is ok. Because we select the first one not the right one
public Customer()
{
}
public Customer(IPhoneResolver resolver)
{
this.resolver = resolver;
}
public IPhoneResolver resolver { get; set; }
}
public interface IPhoneResolver
{
string Name { get; set; }
bool DoesSomething();
}
public class PhoneResolver : IPhoneResolver
{
public string Name { get; set; } = "test";
public bool DoesSomething()
{
return true;
}
}
}
So because the first constructor is null there is an issue.
I need a way to resolve the correct constructor. I have the interface via the RegistryRecord and (type) InterfaceType. I need to find a way to get a constructor that matches that types parameters.

How can I avoid code duplication of a static method in multiple classes

I have multiple classes containing duplicated code, especially members and most important a static method that will create a new instance of the class and returning this instance: either a previously created instance registered in a dictionary or a new instance by calling the constructor.
An interface is no option, because I have the static method. I tried to solve the problem by introducing a base class that implements this static method, but I can not find a way to create and return a spefific child class properly.
Below is a code example of the current situation with class A and class B showing duplicated code.
public class A
{
private static readonly IDictionary<string, A> Registry = new Dictionary<string, A>();
public string Name { get; set; }
public A(string name)
{
this.Name = name;
}
public static A GetA(string instanceName)
{
lock (Registry)
{
if (!Registry.TryGetValue(instanceName, out var newInstance))
{
newInstance = new A(instanceName);
}
return newInstance;
}
}
}
And then in class B again there is a member Name and the GetX() Method.
public class B
{
private static readonly IDictionary<string, B> Registry = new Dictionary<string, B>();
public string Name { get; set; }
public B(string name)
{
this.Name = name;
}
public static B GetB(string instanceName)
{
lock (Registry)
{
if (!Registry.TryGetValue(instanceName, out var newInstance))
{
newInstance = new B(instanceName);
}
return newInstance;
}
}
}
Is there a possibility to avoid this kind of code duplication by introducing a base class or any other way?
This might be a little cleaner:
public class B: RegistryInstance<B>
{
public string Name { get; set; }
public B(string name)
{
this.Name = name;
}
}
public class A : RegistryInstance<A>
{
public string Name { get; set; }
public A(string name)
{
this.Name = name;
}
}
public abstract class RegistryInstance<T> where T:class
{
protected static readonly IDictionary<string, T> Registry = new Dictionary<string, T>();
public static T GetInstance(string instanceName)
{
lock (Registry)
{
if (!Registry.TryGetValue(instanceName, out var newInstance))
{
newInstance = (T)Activator.CreateInstance(typeof(T), new object[] { instanceName });
Registry.Add(instanceName, newInstance);
}
return newInstance;
}
}
}
Are you looking for a generic base class?
public abstract class BaseRegistryGetter<T>
{
private static readonly IDictionary<string, T> Registry = new Dictionary<string, T>();
public string Name { get; set; }
public BaseRegistryGetter(string name)
{
this.Name = name;
}
public static T GetValue (string instanceName, Func<string, T> creator) {
lock (Registry)
{
if (!Registry.TryGetValue(instanceName, out var newInstance))
{
newInstance = creator(instanceName);
}
return newInstance;
}
}
}
And then use it like this:
public class A : BaseRegistryGetter<A>
{
public A(string name) : base(name)
{
}
public static A GetA(string instanceName)
{
return BaseRegistryGetter<A>.GetValue(instanceName, s => new A(s));
}
}
The source for the awkward approach to make sure there is a string-constructor for A can be found here.
I think this should work. You can adapt it to fit your needs. Also, there was a bug in your code: you forgot to add to the Registry when you were creating a new instance.
class Program
{
static void Main(string[] args)
{
A a1 = A.GetInstance("a");
A a2 = A.GetInstance("aa");
A a3 = A.GetInstance("a");
B b1 = B.GetInstance("a");
B b2 = B.GetInstance("aa");
B b3 = B.GetInstance("a");
Console.WriteLine(a1 == a2); //false
Console.WriteLine(a1 == a3); //true
Console.WriteLine(b1 == b2); //false
Console.WriteLine(b1 == b3); //true
Console.ReadKey();
}
}
public class A : Generic<A>
{
public A(string name)
: base(name)
{
}
}
public class B : Generic<B>
{
public B(string name)
: base(name)
{
}
}
public abstract class Generic<T> where T : Generic<T>
{
private static readonly IDictionary<string, T> Registry = new Dictionary<string, T>();
public string Name { get; set; }
public Generic(string name)
{
this.Name = name;
}
public static T GetInstance(string instanceName)
{
lock (Registry)
{
if (!Registry.TryGetValue(instanceName, out var newInstance))
{
newInstance = (T)Activator.CreateInstance(typeof(T), instanceName);
Registry.Add(instanceName, newInstance);
}
return newInstance;
}
}
}
All the other answers try to solve this with generics, but it might be the case you wouldn't want to do this. First, it could be an unnecessary restriction further along that could end up causing variance issues. Second, it only solves one level of inheritance, if there is more, you are stuck again with the same problem:
class Base<T> { ... }
class A: Base<A> { ... }
class B: A { //How does the generic base class help? }
There are general solutions without the use generics that entails just a little code duplication. One could be the following:
public class Base
{
static readonly IDictionary<string, Base> Registry =
new Dictionary<string, Base>();
protected static Base GetBase(string instanceName,
Func<Base> creator)
{
lock (Registry)
{
if (!Registry.TryGetValue(instanceName, out var newInstance))
{
newInstance = creator();
}
return newInstance;
}
}
//...
}
And now yor derived types can impement a strongly typed delegated method:
public class A: Base
{
public A(string instanceName)
:base(instanceName)
{
}
public static A GetA(string instanceName)
=> GetBase(instanceName, () => new A(instanceName)) as A;
}
public class B: Base
{
public B(string instanceName)
:base(instanceName)
{
}
public static B GetB(string instanceName)
=> GetBase(instanceName, () => new B(instanceName)) as B;
}

Designing a custom PetaPoco mapper that supports HeadSprings Enumeration Class

I'm attempting to create a mapper so PetaPoco can hydrate and persist POCOs with Enumeration class properties. See more about Enumeration classes here or here.
For instance, Take this class.
public class PetType : Headspring.Enumeration<PetType>
{
public static readonly PetType Frog = new PetType(1, "Frog");
public static readonly PetType Cat = new PetType(2, "Cat");
public static readonly PetType Fish = new PetType(3, "Fish");
public static readonly PetType Dog = new PetType(4, "Dog");
private PetType(int value, string displayName) : base(value, displayName) { }
}
Which can be used like so:
var MyPet = PetType.Dog;
Here is the Poco I want to hydrate/persist with the database:
public class Pet
{
public int ID { get; set; }
public string OwnerName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PetName{ get; set; }
public PetType PetType{ get; set; }
}
I have designed a custom mapper that will work with PetType:
class EnumClassMapper : PetaPoco.StandardMapper
{
public override Func<object, object> GetFromDbConverter(System.Reflection.PropertyInfo targetProperty, Type sourceType)
{
if (targetProperty.PropertyType == typeof(PetType))
{
return (x) => PetType.FromValue((int) x);
}
return base.GetFromDbConverter(targetProperty, sourceType);
}
public override Func<object, object> GetToDbConverter(System.Reflection.PropertyInfo sourceProperty)
{
if (sourceProperty.PropertyType == typeof(PetType))
{
return (x) => ((PetType)x).Value;
}
return base.GetToDbConverter(sourceProperty);
}
}
However suppose I create another Enumeration subclass for disposition.
public class Disposition: Headspring.Enumeration<Disposition>
{
public static readonly Friendly = new Disposition(1, "Friendly");
public static readonly Timid = new Disposition(2, "Timid");
public static readonly Aggressive = new Disposition(3, "Aggressive");
private Disposition(int value, string displayName) : base(value, displayName) { }
}
I don't want to have to update my mapper every time I create a new subclass of the Enumeration class. I prefer that the mapping code could recognize that the property type is a descendent of the Enumeration class, and map accordingly. I assume the answer is to make use of reflection, but I don't know how to proceed.
What about
public class EnumClassMapper<T> : PetaPoco.StandardMapper
where T : Headspring.Enumeration<T>
{
public override Func<object, object> GetFromDbConverter(System.Reflection.PropertyInfo targetProperty, Type sourceType)
{
return (x) => Enumeration<T, int>.FromValue((int) x);
}
public override Func<object, object> GetToDbConverter(System.Reflection.PropertyInfo sourceProperty)
{
return (x) => ((T)x).Value;
}
}
var builder = DatabaseConfiguration.Build()
.UsingConnectionStringName("sqlite")
.UsingDefaultMapper<ConventionMapper>(m =>
{
m.FromDbConverter = (targetProperty, sourceType) =>
{
if (targetProperty == null)
return null;
var t = targetProperty.PropertyType;
if (t.BaseType == null || ! t.BaseType.IsGenericType)
return null;
if (t.BaseType.GetGenericTypeDefinition() != typeof(Headspring.Enumeration<>))
return null;
return ((IMapper)Activator.CreateInstance(typeof(EnumClassMapper<>).MakeGenericType(t))).GetFromDbConverter(targetProperty, sourceType);
};
m.ToDbConverter = sourceProperty =>
{
if (sourceProperty == null)
return null;
var t = sourceProperty.PropertyType;
if (t.BaseType == null || !t.BaseType.IsGenericType)
return null;
if (t.BaseType.GetGenericTypeDefinition() != typeof(Headspring.Enumeration<>))
return null;
return ((IMapper)Activator.CreateInstance(typeof(EnumClassMapper<>).MakeGenericType(t))).GetToDbConverter(sourceProperty);
};
});
var db = builder.Create();

Categories