Add singleton to IServiceCollection dynamically in a loop - c#

Hi I have an interface which is implemented by multiple classes.
public interface IHuman
{
void Talk();
}
public class AdultHuman
{
public void Talk()
{
Console.Writeline("Hi");
}
}
public class BabyHuman
{
public void Talk()
{
Console.Writeline("Babble");
}
}
public enum HumanEnums
{
Adult,
Baby
}
Currently in my startup add on I have
services.AddSingleton<AdultHuman>();
services.AddSingleton<BabyHuman>();
We are constantly adding different implementations of IHumans so I would like my start up add on to be dynamic to add the singletons with a forloop looping through the values of the HumanEnums so it would look like this
var enumTypes = Enum.GetValues(typeof(ActionTypes));
foreach(var enum in enumTypes)
{
var type = typeof(IHuman);
// namespace + name of class i.e MyProgram.Humans.BabyHuman
var typeName = $"{type.Namespace}.{action}Human";
var t = Type.GetType(typeName, true);
services.AddSingleton< --something here-- >();
}
How would I achieve this?
P.S. Also it would be helpful if instead of looping through the enums, I could find all implementations of IHuman and loop through that.

Thanks guys I was able to solve it with your help! I didnt realize that you could add single with types instead of classes. So I used AddSingleton(typeof(Adult)); instead of AddSingleton();
var humanTypes = typeof(IHuman).
GetTypeInfo().Assembly.DefinedTypes
.Where(t => typeof(IHuman).GetTypeInfo().IsAssignableFrom(t.AsType()) && t.IsClass)
.Select(p => p.AsType());
foreach(var humanType in humanTypes )
{
services.AddSingleton(humanType);
}

Related

how to know all generics type values in c#?

i have simple class like below :
public class HelathCheck<T>
{
public static Dictionary<string, CircuitBreakerPolicy<T>> pollyPolicies = new Dictionary<string, CircuitBreakerPolicy<T>>();
}
i am adding value like below to this Policies dynamically.
HelathCheck<ClassA>.pollyPolicies.Add("SportsAPI1", Policy1);
HelathCheck<ClassB>.pollyPolicies.Add("SportsAPI2", Policy2);
HelathCheck<ClassC>.pollyPolicies.Add("SportsAPI3", Policy3);
HelathCheck<ClassD>.pollyPolicies.Add("SportsAPI4", Policy4);
i am storing some CircuitBreakerPolicy in Dictionary object so i can use get value later.
now i want to know all value which is stored into SoapPollyPolicies from one method
something like this, basically how do i know values store in all class?
can you please give me some hints? Thanks ( is this is right question?, not sure )
// here i do not want to pass classA, classB..etc, just one line and all values, i wanted to get it.
foreach (var item in HelathCheck<??>.SoapPollyPolicies)
{
response.Add(item.Key, item.Value);
}
may be something like this but only class ==>
https://expertcodeblog.wordpress.com/2018/02/07/c-get-all-types-that-implement-an-interface/
the closest i can reach to this is :
var type = typeof(HelathCheck<>);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
foreach (var item1 in types)
{
//do stuff
var prop = item1.GetField("pollyPolicies "); // FaultResponse is one of object from SOAP resposne.
//var value= prop.GetValue("pollyPolicies");
}
You do not need generics here. You say they are a common base class so do
public class HelathCheck
{
public static Dictionary<string, BreakerBase> pollyPolicies = new Dictionary<string, BreakerBase>();
}
and put all the obects in that one dictioanry
The most accurate way of doing this would be to create an interface for your test classes, and have each test class implement that interface.
public interface ITestClass { }
public class TestClassA : ITestClass
{
}
public class TestClassB : ITestClass
{
}
In your HealthCheck class, add a type constraint:
public class HealthCheck<T> where T : ITestClass
{
public static Dictionary<string, T> Policies { get; set; }
}
Now, you can then enumerate over the added items using ITestClass as your type parameter like so:
public class Consumer
{
public Consumer()
{
HealthCheck<TestClassA>.Policies.Add("API1", new TestClassA());
HealthCheck<TestClassB>.Policies.Add("API2", new TestClassB());
foreach (var policy in HealthCheck<ITestClass>.Policies)
{
Console.WriteLine(policy.Key);
Console.WriteLine(policy.Value);
}
}
}

How to avoid repetition of setIdGenerator for all the classes in c# mongodb?

public static void RegisterMappings()
{
BsonClassMap.RegisterClassMap<Job>(map =>
{
map.AutoMap();
map.IdMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance);
});
BsonClassMap.RegisterClassMap<JobRun>(map =>
{
map.AutoMap();
map.IdMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance);
});
}
In all the classes in my project, I have Id property. I have to set idgenerator for all the classes. How to avoid the repitition of code?
If you define for example interface ISetIdGenerator for all classes you need to register it, you could do following:
var types = Assembly.GetExecutingAssembly().ExportedTypes
.Where(x=>x.GetInterfaces().Contains(typeof(ISetIdGenerator)));
foreach (var type in types)
{
var classMap = new BsonClassMap(type);
classMap.AutoMap();
classMap.IdMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance);
BsonClassMap.RegisterClassMap(classMap);
}
Sure, you could avoid this interface and register mapper for all classes in solution, but i think it's a little bit overhead.
You can create a IClassMapConvention and register that.
public class IdGeneratorClassMapConvention : ConventionBase, IClassMapConvention
{
public void Apply(BsonClassMap classMap)
{
map.AutoMap();
map.IdMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance);
}
}
and register it
ConventionRegistry.Register("IdGeneratorConvention", new ConventionPack
{
new IdGeneratorClassMapConvention()
}, t => !t.GetTypeInfo().IsEnum);
This will apply it to all non-enum types. You still need to call BsonClassMap.RegisterClassMap<T>() iirc. The second parameter t => !t.GetTypeInfo().IsEnum is a filter to limit the types.

c# - Initialize List<> from another class

I have defined a class which has List<>. I have shortened my Code. It is too large. There are too many List<>& in Method1() there is lots of code. Here is my code :-
public class Time : ITime
{
public List<Table1> Setts1 = new List<Table1>();
public List<Tabl2> Setts2 = new List<Table2>();
public void LoadSettings1(int companyId)
{
Setts1 = ctx.tblSett1.Where(a => a.CompanyId == companyId).Select(a => a).ToList();
}
public double Method1()
{
var data = Setts1.Where(m => m.SetType == "TYPE1").Select(m => m.Value1).FirstOrDefault();
......
......
}
}
I want to use Method1() in another class. My issue is Setts1 which is preloaded in the Time Class. So when it is used in within the Time class it has Records. But when i call it from another class obviously Setts1 will have no records. I tried to initialize it from another class like this :-
public class Class
{
.....
Time cls = new Time();
cls.Setts1 = ....;
cls.Method1();
}
But Setts1 shows no records when in Method1. How to initialize the List<> from another class?
Exposing field members of a class, outside of the class is not a good practice. So I recommend using properties like this:
//Mark the field member as private
private List<Table1> _Setts1 = new List<Table1>();
//Use Property to access the field outside of the class
public List<Table1> Setts1
{
get
{
if (_Setts1==null || _Setts1.Count()==0) //or any other logic you need
{
//Initialize the field memeber
_Setts1 = ctx.tblSett1.Where(a => a.CompanyId == companyId).Select(a => a).ToList();
}
return _Setts1
}
}
This way you can forget about methods like LoadSettings1 and it doesn't matter whether you use the Setts property inside the class or outside, it will be initialized at the right time.
You have to call 'LoadSettings1(int companyId)'. This is the method which brings the records and populates your 'List'.
public class Class
{
.....
Time cls = new Time();
cls.LoadSettings1(1);
cls.Setts1 = ....;
cls.Method1();
}
public class Something
{
private Time cls = new Time();
public Something(int companyId)
{
cls.LoadSettings1(companyId);
}
public void CallMethod1()
{
cls.Method1();
}
}
Something like this? Using constructor for your "other class" to LoadSettings.
cls.Setts1 = ....;
Actually I don't see how your code would not work, even if as Hossein said, it's bad practice. Look into how you're setting cls.Setts1 (the .... part). That's most probably the culprit

How to get items from generics using expression?

I have an Ability class which looks like this
L1.
public class Ability
{
public int Id { get; set; }
public string Name {get; set;}
}
There are also many more enumlike classes that have Id and Name. So im writing Generic class to have less work later with them.
L2.
public class EnumRepository<TEnum>where TEnum : class
{ ... }
One method od said class looks like this:
L3.
public IEnumerable<SelectListItem> ToSelectListItem(
Expression<Func<TEnum, IEnumerable<Tuple<string, int>>>> text = null)
{
IQueryable<TEnum> query = dbSet;
var ret = new List<SelectListItem>();
if (text != null)
{
var res = query.SelectMany(text);
foreach (var tuple in res)
{
ret.Add(new SelectListItem()
{
Text = tuple.Item1,
Value = tuple.Item2.ToString()
});
}
}
return ret;
}
But I wore sth that I dont know how to use...
L4.
ret.Abilities = _unitOfWork.AbilityRepository
.ToSelectListItem( !what goes here?! )
Here are my questions:
What to put into metod argument in L4. to make it work?
Is there better way to do this?
Is it worth to do it?
In my old aproach I wrote ToSelectListItems in each class of this type. And using it was simple, like this ret.Abilities = Ability.ToSelectListItems() <- static method. But I had to do write this code in every class = hard to maintain and dumb way of doing things.
Assuming I understand your problem correctly, here goes:
What to put into metod argument in L4. to make it work?
Assuming for some reason you want to go ahead with your setup (please see below), you'd have to do something along those lines:
ret.Abilities =
_unitOfWork.AbilityRepository
.ToSelectListItem(item => new[] { new Tuple<String, int> (
(YourAbilityClass)item.Id,
(YourAbilityClass)item.Name)) };
which is slightly counterproductive, as you'd need to maintain part of your repository logic in every call.
Is there better way to do this?
Define better :). The way I would approach is as follows:
1) Define a new base class for all your entities, something like
public class BaseClass
{
public int Id { get; set; }
public String Name { get; set; }
}
and have all your relevant entities inherit from it:
public class Ability : BaseClass
{
}
(alternatively use a common interface - that depends on your design, so I can't make an informed suggestion here)
2) Then constraint your repositories to use BaseClass, like so:
public class EnumRepository<TEnum>where TEnum : BaseClass { ... }
3) Finally you can have
public IEnumerable<SelectListItem> ToSelectListItem()
{
return dbSet.Select(bc => new SelectListItem()
{
Text = bc.Name,
Value = bc.Id.ToString()
})
.ToArray();
}
and call it like so:
ret.Abilities = _unitOfWork.AbilityRepository.ToSelectListItem();
Is it worth to do it?
It's always hard to make fool-proof comments against someone else's design, if we're only shown a very small percent of it. Make your own decision - I do believe my suggestion might be a bit simpler in the long run, assuming it fits your needs.

How to mock an interface that extends IEnumerable

I'm using Moq and I have the following interface:
public interface IGameBoard : IEnumerable<PieceType>
{
...
}
public class GameBoardNodeFactory
{
public virtual GameBoardNode Create (int row, int column, IGameBoard gameBoard)
{
...
}
}
Then I have a test like this:
var clonedGameBoardMock = new Mock<IGameBoard> (MockBehavior.Loose);
var gameBoardNodeFactoryMock = new Mock<GameBoardNodeFactory> ();
gameBoardNodeFactoryMock.Setup (x =>
x.Create (
position.Row,
position.Column,
clonedGameBoardMock.Object)).Returns (new GameBoardNode { Row = position.Row, Column = position.Column });
But then gameBoardNodeFactoryMock.Object.Create (position.Row, position.Column, clonedGameBoardMock.Object) throws a NullReferenceException. I tried to create a mock for the IGameBoard such that it doesn't extend IEnumerable<PieceType> interface and then it works.
Any help is appreciated.
You would need to create a Setup for GetEnumerator() if it's being called. Something like:
var mockPieces = new List<PieceType>;
clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(mockPieces.GetEnumerator());
Note sure if that's the issue in this case, but worth noting if you ever need to mock IEnumerable<T>.
The answer by #DanBryant was also the key to our solution. However, the enumerator in that case might be accidentally reused. Instead, I suggest using:
clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(() => mockPieces.GetEnumerator());
Here's a full repro (new class library using NUnit 2.6.4 and Moq 4.2):
public interface IMyThing<T> : IEnumerable<T>
{
string Name { get; set; }
IMyThing<T> GetSub<U>(U key);
}
public interface IGenericThing
{
string Value { get; set; }
}
public class Pet
{
public string AnimalName { get; set; }
}
public class Unit
{
public IEnumerable<Pet> ConvertInput(IMyThing<IGenericThing> input)
{
return input.GetSub("api-key-123").Select(x => new Pet { AnimalName = x.Value });
}
}
[TestFixture]
public class Class1
{
[Test]
public void Test1()
{
var unit = new Unit();
Mock<IMyThing<IGenericThing>> mock = new Mock<IMyThing<IGenericThing>>();
Mock<IMyThing<IGenericThing>> submock = new Mock<IMyThing<IGenericThing>>();
var things = new List<IGenericThing>(new[] { new Mock<IGenericThing>().Object });
submock.Setup(g => g.GetEnumerator()).Returns(() => things.GetEnumerator());
mock.Setup(x => x.GetSub(It.IsAny<string>())).Returns(submock.Object);
var result = unit.ConvertInput(mock.Object);
Assert.That(result, Is.Not.Null.And.Not.Empty);
Assert.That(result, Is.Not.Null.And.Not.Empty); // This would crash if the enumerator wasn't returned through a Func<>...
}
}
For what it's worth / to make this question pop up to that one lone Googler with the same problem I had: the above is an abstracted version of the Couchbase .NET client's IView<T> interface, which also implements IEnumerable<T>.
A null reference in this situation usually means your setup was never met. Meaning it was never called with the exact values you set it up for. To debug this I would make your match less constraining by using It.IsAny() and so on to make sure the test will match on any call to the mocked function. In most cases this is good enough. Any reason why your are trying to match on specific values?
Okay if anyone is interested, I updated Moq to version 4 and now everything works as expected.

Categories