I have a abstract class Enumeration which implements the IComparable interface.
public abstract class Enumeration : IComparable
{
[JsonConstructor]
protected Enumeration(int id, string name)
{
Id = id;
Name = name;
}
public int Id { get; }
public string Name { get; }
public static implicit operator Enumeration(string name)
{
return GetAll<Enumeration>().FirstOrDefault(i => i.Name == name);
}
public static IEnumerable<TEnumeration> GetAll<TEnumeration>() where TEnumeration : Enumeration
{
var fields = typeof(TEnumeration).GetFields(
BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
return fields.Select(fieldInfo => fieldInfo.GetValue(null)).Cast<TEnumeration>();
}
I have created SampleStatus.cs class which is inherits from Enumeration.cs class.
public class SampleStatus : Enumeration
{
public static readonly SampleStatus Completed = new SampleStatus(1, nameof(Completed));
public static readonly SampleStatus Deleted = new SampleStatus(2, nameof(Deleted));
public SampleStatus(int id, string name) : base(id, name)
{
}
}
I have created unit test class for SampleStatus.cs class.
[TestMethod]
public void TestMethod()
{
// arrange and act
var result = (Enumeration)SampleStatus.GetAll<SampleStatus>().Single(x => x.Id == 1).Name; // output is returning null.
// assert
Assert.AreEqual("Completed", result);
}
When I call GetAll method which is returning null. I have mocked GetAll and Implicit operator method in the above code.
In provided sample GetAll does not return null (if it did - I would get NullReferenceException). Issue with the sample is in the cast to Enumeration. SampleStatus.GetAll<SampleStatus>().Single(x => x.Id == 1).Name results in Completed which you then cast to Enumeration. Since the implicit conversion exists and the base class does not have definition for Completed (Enumeration)"Completed" ends up being null (i.e. GetAll<Enumeration>().FirstOrDefault(i => i.Name == name))).
You can fix it with removing cast for example:
[TestMethod]
public void TestMethod()
{
// arrange and act
var result = SampleStatus.GetAll<SampleStatus>().Single(x => x.Id == 1).Name; // output is returning null.
// assert
Assert.AreEqual("Completed", result);
}
Related
I have a code like this:
using System;
using System.Collections.Generic;
using System.Linq;
public interface IMyString
{
string Id {get;set;}
};
public class MyString : IMyString
{
public string Id {get;set;}
}
public static class Extensions
{
public static IEnumerable<IMyString> WithId(this IEnumerable<IMyString> source, string id)
{
return source.Where(x => x.Id == id);
}
}
public class Program
{
private static List<T> GetMyStrings<T>(string key, List<T> input)
where T: IMyString
{
return input.WithId(key).ToList();
}
public static void Main()
{
var foo = new List<MyString>{ new MyString { Id = "yes"}, new MyString { Id = "no" } };
var result = GetMyStrings("yes", foo);
var result2 = foo.WithId("no");
Console.WriteLine(result2);
}
}
Why does input.WithId(key).ToList() cause a syntax error, while foo.WithId("no") is OK? Is there a way to make the method GetMyStrings work?
Without the context of your code it is a little difficult to help too much, but your type constraints for the two methods are different. You have two options:
Option 1:
public static class Extensions
{
public static IEnumerable<T> WithId<T>(this IEnumerable<T> source, string id) where T: IMyString
{
return source.Where(x => x.Id == id);
}
}
Option 2:
private static List<IMyString> GetMyStrings(string key, List<IMyString> input)
{
return input.WithId(key).ToList();
}
public static void Main()
{
var foo = new List<IMyString>{ new MyString { Id = "yes"}, new MyString { Id = "no" } };
var result = GetMyStrings("yes", foo);
var result2 = foo.WithId("no");
Console.WriteLine(result2);
}
Here is a dotnetfiddle with the second option as a working piece of code:
public static IEnumerable<T> WithId<T>(this IEnumerable<T> source, string id)
where T : IMyString
{
return source.Where(x => x.Id == id);
}
I'm using Dapper to hydrate a C# class. I recently moved from collections of string constants to "enumeration classes" as defined here: https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types
My enumeration looks like this:
public abstract class Enumeration : IComparable
{
public string Name { get; }
protected Enumeration(string name)
{
Name = name;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration
{
var fields = typeof(T).GetFields(BindingFlags.Public |
BindingFlags.Static |
BindingFlags.DeclaredOnly);
return fields.Select(f => f.GetValue(null)).Cast<T>();
}
public static IEnumerable<T> ToSortedEnumerable<T>() where T : Enumeration
{
List<T> values = GetAll<T>().ToList();
values.Sort();
return values;
}
public int CompareTo(object other) =>
string.Compare(Name, ((Enumeration) other).Name, StringComparison.Ordinal);
public static implicit operator string(Enumeration enumeration)
{
return enumeration?.ToString();
}
public static bool operator ==(Enumeration e1, Enumeration e2)
{
return Equals(e1, e2);
}
public static bool operator !=(Enumeration e1, Enumeration e2)
{
return !Equals(e1, e2);
}
public static bool HasValue<T>(string valueToCheck) where T : Enumeration
{
return Enumeration.GetAll<T>().Any(x => x.Name.Equals(valueToCheck, StringComparison.OrdinalIgnoreCase));
}
public static bool TryGetEnumeration<T>(string valueToCheck, out T result) where T : Enumeration
{
result = Enumeration.GetAll<T>()
.FirstOrDefault(
x => x.Name.Equals(valueToCheck, StringComparison.OrdinalIgnoreCase));
return result != null;
}
public static T GetEnumeration<T>(string valueToCheck) where T : Enumeration
{
var result = Enumeration.GetAll<T>()
.FirstOrDefault(
x => x.Name.Equals(valueToCheck, StringComparison.OrdinalIgnoreCase));
if (result == null)
{
throw new ArgumentException($"Invalid {typeof(T).Name}: {valueToCheck}");
}
return result;
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
return false;
bool typeMatches = this.GetType() == obj.GetType();
bool valueMatches = this.Name.Equals(otherValue.Name);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return 539060726 + EqualityComparer<string>.Default.GetHashCode(this.Name);
}
public override string ToString() => this.Name;
}
and my Race class looks like this:
public class Race : Enumeration
{
public static Race White = new Race("White");
public static Race Hawaiian = new Race("Native Hawaiian");
public static Race Filipino = new Race("Filipino");
public static Race Black = new Race("Black / African American");
public static Race Chinese = new Race("Chinese");
public static Race Japanese = new Race("Japanese");
public static Race Korean = new Race("Korean");
public static Race Vietnamese = new Race("Vietnamese");
public static Race AsianIndian = new Race("Asian Indian");
public static Race OtherAsian = new Race("Other Asian");
public static Race Samoan = new Race("Samoan");
public static Race AmericanIndian = new Race("American Indian");
public static Race AlaskaNative = new Race("Alaska Native");
public static Race Guamanian = new Race("Guamanian");
public static Race Chamorro = new Race("Chamorro");
public static Race OtherPacificIslander = new Race("Other Pacific Islander");
public static Race Other = new Race("Other");
public Race(string name) : base(name)
{ }
}
My simplified Person object looks like this:
public class Person
{
public Person(Guid personId, Race race){
PersonId = personId;
Race = race;
}
public Race Race {get;}
public Guid PersonId {get;}
}
Here's a simplified Dapper command (talking to postgresql) that works (PersonId is hydrated correctly), but Race is always NULL.
return connection.Query<Person>(sql: #"
SELECT person_id as PersonId
,race
FROM public.people");
I have tried adjusting my SQL to this:
return connection.Query<Person>(sql: #"
SELECT person_id as PersonId
,race as Name
FROM public.people");
but that also results in a null value for Race.
Is what I'm attempting even possible? Do I have to do a splitOn for this? I've avoided that because my real class has dozens of such properties and they'd all have to be Name and . . . well, I just didn't want to go there if I was missing something silly here. I honestly kind of thought that the
public static implicit operator string(Enumeration enumeration)
would take care of this for me.
Thoughts anyone? Help is always appreciated.
Maybe this is too simple, but, the column names you are selecting need to match the properties in the class to which you are mapping, otherwise Dapper won't know how to make the mappings match.
So if your class is:
public class Person
{
public Race Race {get;}
public Guid PersonId {get;}
}
Then your query would need to match:
return connection.Query<Person>(sql: #"
SELECT
Race
, person_id as PersonId
FROM public.people");
Note the upper case R on Race. (And for good measure, I like to keep them in the same order too, although I'm not sure this matters.)
That aside, if you execute your query directly against the database, do you get back the results you expect?
Ok, figured it out. Two things:
First, splitOn is the way to do this. A different, but related final version looks like this:
return connection.Query<Program,
AssistanceProgramCategory,
AssistanceProgramType,
AssistanceProgramLegalType,
ProgramAuthority,
Program>(sql: Constants.SqlStatements.SELECT_PROGRAMS_SQL,
(program, category, programType, legalType, authority) =>
{
program.AssistanceCategory = category;
program.ProgramType = programType;
program.ProgramLegalType = legalType;
program.Authority = authority;
return program;
}, splitOn: "Name,Jurisdiction");
where AssistanceProgramCategory, AssistanceProgramType, and AssistanceProgramLegalType are all children of Enumeration.
Second, the SQL does have to deliver the columns up with Name, as in:
SELECT global_id as GlobalId
,tier
,program_description as Name
,program_type as Name
,program_legal_type as Name
,jurisdiction as Jurisdiction
,customer_id as CustomerId
,program_name as ProgramNameForJurisdiction
,program_description as ProgramName
FROM public.assistance_programs
Third, I only had to put "Name" in the splitOn once - every instance of Name caused a new object to be created.
Finally, I had to swap Jurisdiction and CustomerId because CustomerId can be null, and when NULL, it doesn't fire the final hydration into ProgramAuthority. Jurisdiction is always present, so problem solved by swapping the columns in the SQL.
Hope this helps someone.
All the best,
V
I have multiple data-points and an associated data-processor for each.
public interface IDataPointProcessor<T> where T : DataPointInputBase
{
DataPointOutputBase GetOutput(T input);
}
I load a list of data points from a file and wish to process them using its single associated processor.
foreach (DataPointInputBase item in input.DataPoints)
{
//assuming item coming in is of type 'X' how do I get correct processor
var type = typeof(IDataPointProcessor<X>);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p) && !p.IsAbstract);
IDataPointProcessor<X> matchedType = ??
}
How do I solve for 'X' so I can instantiate it and process the input?
Update #1
Combining answers from below from Slava and Lucky I get the following, but it throws an exception - 'Object does not match target type.' even though it all seems to match up ok in debugger. Is it possible to cast as IDataPointProcessor<> and call interface method cleanly, ie: instance.GetOutput(item);
foreach (DataPointInputBase item in input.DataPoints)
{
Type typeGenArg = item.GetType();
Type typeInterfaceGen = typeof(IDataPointProcessor<>).MakeGenericType(typeGenArg);
Type type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => typeInterfaceGen.IsAssignableFrom(x) && !x.IsAbstract)
.FirstOrDefault();
Type genericType = typeof(IDataPointProcessor<>);
Type dependedGenericType = genericType.MakeGenericType(typeof(DataPointInputBase));
var method = dependedGenericType.GetMethod("GetOutput");
var instance = Activator.CreateInstance(type);
//currently throws:System.Reflection.TargetException: 'Object does not match target type.'
var result = method.Invoke(instance, new object[] { item });
//Ideally I want to do this and avoid the magic strings etc
//var temp_output = instance.GetOutput(item);
}
Update #2
To keep things moving I've hard coded the type 'Age_Input' to validate the thing works. What am I missing to call the hard coded bit dynamically?
I should be able to cast instance to IDataPointProcessor<IDataPointInput> and call GetOutput() on the interface
foreach (IDataPointInput item in input.DataPoints)
{
Type typeGenArg = item.GetType();
Type typeInterfaceGen = typeof(IDataPointProcessor<>).MakeGenericType(typeGenArg);
Type type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => typeInterfaceGen.IsAssignableFrom(x) && !x.IsAbstract)
.FirstOrDefault();
Type genericType = typeof(IDataPointProcessor<>);
Type dependedGenericType = genericType.MakeGenericType(typeof(IDataPointInput));
var instance = Activator.CreateInstance(type);
if (instance is IDataPointProcessor<Age_Input>)//hard-coded
{
var processor = instance as IDataPointProcessor<Age_Input>;
Age_Input temp = item as Age_Input;
var result = processor.GetOutput(temp);
}
if (instance is DataPointProcessorBase<DataPointInputBase>)
{
//false
}
if (instance is IDataPointProcessor<DataPointInputBase>)
{
//false
}
if (instance is IDataPointProcessor<IDataPointInput>)
{
//false - shouldn't this work?
}
}
Age_Input is a trivial class, inheriting from a dumb base class and an empty interface
public class Age_Input : DataPointInputBase, IDataPointInput
{
public int AgeExact { get; set; }
}
public class DataPointInputBase : IDataPointInput
{
}
public interface IDataPointInput
{
}
Processor class is similarly simple
public abstract class DataPointProcessorBase<T> : IDataPointProcessor<T> where T : IDataPointInput, new()
{
//public abstract DataPointOutputBase GetOutput(DataPointInputBase input);
public abstract DataPointOutputBase GetOutput(T input);
}
public interface IDataPointInput
{
}
public interface IDataPointProcessor<IDataPointInput>
{
DataPointOutputBase GetOutput(IDataPointInput input);
}
Firstly, you should make covariant your interface like this.
public interface IDataPointProcessor<in T> where T : DataPointInputBase
{
DataPointOutputBase GetOutput(T input);
}
You should retrieve types which is implemented by IDataPointProcessor<>, then you should create the instance of retrieved type and invoke the method of generic type.
Type genericType = typeof(IDataPointProcessor<>);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => genericType.IsAssignableFrom(p) && !p.IsAbstract).ToList();
var dependedGenericType = genericType.MakeGenericType(typeof(DataPointInputBase));
var method = dependedGenericType.GetMethod("GetOutput");
var instance = Activator.CreateInstance(types[0]);
method.Invoke(instance, new object[] { new DataPointInputBase() });
So as is usually the case, if you can avoid Reflection you're generally better off. I traded a tiny bit of code smell for a much simpler solution.
Essentially I went back to basics and used dumb interfaces, and a helper method on the input that returned a primed instance of the corresponding processor.
Now my big reflection loop is replaced with this:
foreach (IDataPointInput item in input)
{
IDataPointProcessor processor = item.GetProcessor();
IDataPointOutput output = processor.GetOutput();
}
The code smell is this - not an issue
public override IDataPointProcessor GetProcessor()
{
return new Age_Processor(this);
}
Full code below
#region Interfaces
public interface IDataPointProcessor
{
IDataPointOutput GetOutput();
}
public interface IDataPointInput
{
IDataPointProcessor GetProcessor();
}
public interface IDataPointOutput
{
List<string> DebugStrings { get; set; }
}
#endregion
#region Base Classes
public abstract class DataPointProcessorBase : IDataPointProcessor
{
public abstract IDataPointOutput GetOutput();
}
public abstract class DataPointInputBase : IDataPointInput
{
public abstract IDataPointProcessor GetProcessor();
}
public abstract class DataPointOutputBase : IDataPointOutput
{
public List<string> DebugStrings { get; set; }
public DataPointOutputBase()
{
DebugStrings = new List<string>();
}
}
#endregion
public class Age_Output : DataPointOutputBase
{
}
public class Age_Input : DataPointInputBase
{
public int AgeExact { get; set; }
public override IDataPointProcessor GetProcessor()
{
return new Age_Processor(this);
}
}
public class Age_Processor : DataPointProcessorBase
{
public Age_Input Input { get; set; }
public Age_Processor(Age_Input input)
{
Input = input;
}
public override IDataPointOutput GetOutput()
{
Age_Output output = new Age_Output();
if (Input.AgeExact > 30)
{
output.DebugStrings.Add("Getting old");
}
else
{
output.DebugStrings.Add("Still young");
}
return output;
}
}
public class DecisionEngine
{
public void GetDecisions()
{
List<IDataPointInput> input = new List<IDataPointInput>();
input.Add(new Age_Input { AgeExact = 44 });
foreach (IDataPointInput item in input)
{
IDataPointProcessor processor = item.GetProcessor();
IDataPointOutput output = processor.GetOutput();
}
}
}
So I am using Simple.Mocking to Mock some interfaces on my tests. Some methods receive custom objects
public class MyObj
{
public int Attr { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as MyObj);
}
public override int GetHashCode()
{
return Attr;
}
private bool Equals(MyObj myObj)
{
return Attr == myObj.Attr;
}
}
public interface IFoo
{
void Show(MyObj o);
}
public class ObjUnderTest
{
public ObjUnderTest(IFoo foo)
{
var o = new MyObj{ Attr = 1; };
foo.Show(o);
}
}
[TestClass]
public class TestClasse
{
[TestMethod]
public void TestShow()
{
var foo = Mock.Interface<IFoo>();
var myObj = new MyObj { Attr = 1 };
Expect.Once.MethodCall(() => foo.Show(myObj));
var objectUnderTest = new ObjUnderTest(foo);
AssertExpectations.IsMetFor(foo);
}
}
The problems is that test fails always, even when Show is called with a object with Attrequals to 1. It only pass if I write the expect like this:
Expect.Once.MethodCall(()=> foo.Show(Any<MyObj>.Value));
Which is not what I need. I know it fails because those are different objects but I have tried overriding MyObj Equals and GetHashCode with no success.
Any Ideas?
If the desired outcome is to validate the input you can try specifying exptectation with a predicate
Expect.Once.MethodCall(()=> foo.Show(Any<MyObj>.Value.Matching(obj => obj.Attr == 1)));
Source: project readme on Github - Using "wildcard" parameter values
[TestClass]
public class TestClasse {
[TestMethod]
public void TestShow() {
//Arrange
var foo = Mock.Interface<IFoo>();
Expect.Once.MethodCall(()=> foo.Show(Any<MyObj>.Value.Matching(obj => obj.Attr == 1)));
//Act
var objectUnderTest = new ObjUnderTest(foo);
//Assert
AssertExpectations.IsMetFor(foo);
}
}
I think I have a fundamental misunderstanding here. Why does the test fail?
public static class ObjectExtensions
{
public static Action To<T>(this T newValue, T oldValue) where T : class
{
return () => oldValue = newValue;
}
}
public static class Assign
{
public static T TheValue<T>(T theValue)
{
return theValue;
}
}
public class Tests
{
public void Test()
{
var a = new TestType { Name = "a" };
var b = "b";
Assign.TheValue(b).To(a.Name)();
Assert.That(a.Name == "b"); //fails (a.Name == "a")
}
}
public class TestType { public string Name {get;set;} }
It fails because the arguments to To are passed by value.
Just because oldValue is set to "b" doesn't mean that a.Name will be changed at all. In the call To(a.Name), the expression a.Name is evaluated to a string reference, and that reference is passed to the method by value.
That's basic parameter passing in C#. Just using a closure doesn't change that.
What you can do is change the To method like this:
public static Action To<T>(this T newValue, Action<T> setter) where T : class
{
return () => setter(newValue);
}
then change the call to:
Assign.TheValue(b).To(x => a.Name = x)();
Put another way,
var a = new TestType { Name = "a" };
Assign.TheValue(b).To(a.Name)();
is equivalent to
Assign.TheValue(b).To("a")();
just like
int x = 5;
Convert.ToDecimal(x);
is equivalent to
Convert.ToDecimal(5);