Working with specific service from interface - c#

this question is for an app console and .NET framework 4.7
I got 1 interface
public interface IPrepareDataService<T>
{
string Name{ get; }
IEnumerable<T> GetImportData();
}
And 3 classes from this interface
public class APrepareDataService : IPrepareDataService<T>
{
public string Name{ get => "A"; }
public IEnumerable<T> GetImportData()
{
return GetRecords<T>();
}
}
public class BPrepareDataService : IPrepareDataService<T>
{
public string Name{ get => "B"; }
public IEnumerable<T> GetImportData()
{
return GetRecords<T>();
}
}
public class CPrepareDataService : IPrepareDataService<T>
{
public string Name{ get => "C"; }
public IEnumerable<T> GetImportData()
{
return GetRecords<T>();
}
}
T class can be different for every class.
public class AClass
{
public string Name{ get; set; }
public string Desc{ get; set; }
}
public class BClass
{
public string Name{ get; set; }
public string Surname{ get; set; }
}
public class CClass
{
public string Name{ get; set; }
public int Age{ get; set; }
}
In program.cs i've registered services of interface:
private static readonly ServiceProvider _serviceProvider =
new ServiceCollection()
.AddSingleton<IPrepareDataService<AClass>, APrepareDataService>()
.AddSingleton<IPrepareDataService<BClass>, BDataService>()
.AddSingleton<IPrepareDataService<CClass>, CPrepareDataService>()
Problem comes now, in Main function i get from config file a value that defines what service im gonna use:
var prepareDataService = _serviceProvider.GetServices<IPrepareDataService<AClass>>().First(x => x.Name.ToUpper() == ValueFromConfig.ToUpper());
Problem is that i have to set an specific class when i try to get service:
_serviceProvider.GetServices<IPrepareDataService<AClass>>
, so if ValueFromconfig = A, it's gonna work fine, but if it's B it doenst works in a right way.
Question is: is there any way to GetService for the interface related with the value in config if value = A Then interface for AClass, if B interface for BClass?
Thanks in advance

You can do this once, or at runtime multiple times.
To do it multiple times (And I think this is what you want), you can use the GetService overload to pass in a type rather than using the generic. Something like :
var configValue = "AClass";
var requiredType = configValue switch
{
"AClass" => typeof(IPrepareDataService<AClass>),
};
_serviceProvider.GetService(requiredType);
To do it once (e.g. Once your app starts up, read the config, and then you are done), you would need to selectively bind the service collection.

Related

How to configure swashbuckle correct for polymorphism

I have a problem to get the right OpenApi definition aftr update from 5.0.0 to 5.4.1
We had custom Polymorphism filter with 5.0.0 version, but they does not work correct with latest one. So I removed them and started to use GeneratePolymorphicSchemas(). It does what I need for our polymorphic models but not just for them. We have also some other abstract and concrete classes, where we don't need type discriminator. I tried different configurations but without any success. Either the generated definition is wrong or I get error on swagger UI or a server 503 error.
Link to the sample project Sample project
Here are my polimorhic models
namespace SwashbuckleTest.Models
{
public interface ITypeDiscriminator
{
string TypeDiscriminator { get; }
}
public abstract class SurveyStep : ITypeDiscriminator
{
public virtual string Id { get; set; }
public string TypeDiscriminator => GetType().Name;
}
public abstract class SurveyStepResult : ITypeDiscriminator
{
public string Id { get; set; }
public string TypeDiscriminator => GetType().Name;
}
public class BoolStep : SurveyStep
{
private string _id;
public BoolStep()
{
ResultObject = new BoolStepResult();
}
public override string Id
{
get => _id;
set
{
_id = value;
ResultObject.Id = value;
}
}
public string Question { get; set; }
public BoolStepResult ResultObject { get; }
}
public class BoolStepResult : SurveyStepResult
{
public bool Value { get; set; }
}
}
Here other models
namespace SwashbuckleTest.Models
{
public abstract class SomeBaseModel
{
public string BaseValue { get; set; }
}
public class SomeConcreteModel : SomeBaseModel
{
public int ConcreteValue { get; set; }
}
}
and configurations I have tried
options.UseAllOfToExtendReferenceSchemas();
options.GeneratePolymorphicSchemas(t =>
{
var types = t.Is<SurveyStep>() ? new List<Type>() {typeof(BoolStep)}
: t.Is<SurveyStepResult>() ? new List<Type>() {typeof(BoolStepResult)}
: null;
return types;
} , t => t.Is<ITypeDiscriminator>() ? nameof(ITypeDiscriminator.TypeDiscriminator).ToCamelCase() : null);
// or
options.GeneratePolymorphicSchemas(discriminatorSelector: t => t.Is<ITypeDiscriminator>() ? nameof(ITypeDiscriminator.TypeDiscriminator).ToCamelCase() : null);
I found the problem by my self.
The Is<> extension method does not filter abstract classes so we got here endless recursion.
It helped us to generate swagger.json, but we got other problems, that are little bit deeper.

Return interface inherited classes in Generic method

I have 3 objects that are very similar with only a few differences
public class Person
{
public Person(ResourceObject resource)
{
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
// Person-specific property example - Organisation
public string Organisation { get; set; }
}
public class Computer
{
public Computer(ResourceObject resource)
{
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
// Computer-specific property example - OperatingSystem
public string OperatingSystem { get; set; }
}
public class Group
{
public Group(ResourceObject resource)
{
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
// Group-specific property example - Members
public string Members { get; set; }
}
I currently have GetPerson, GetComputer and GetGroup methods that are working but they essentially do the same thing and then call one of the specific object constructors. In an effort to dive into the world of Generics and Interfaces and learn more (as you do) i attempted to create a GetResource<T> method that would do the same job as those 3 methods without all the duplicate code.
I created the IResource Interface to identify common properties:
public interface IResource
{
string ObjectID { get; }
string ObjectType { get; }
IEnumerable<string> PropertyNames { get; }
}
and then attempted to create a GetResource<T> method but got stuck at the return code:
public static T GetResource<T>(string identity) where T : IResource
{
// get resource from system API
// and then return T somehow?
return new T(resourceObject);
}
I thought of changing the return value from T to IResource but i'm still not sure how i would identify which class to return (Perhaps i need a base class? Resource perhaps).
The reason i turned to Generics for this specific situation is if the system API updates and suddenly has a new Location object i don't want to have to create a GetLocation method and then have 4 methods that do exactly the same thing except for one line of code.
Is this the correct use case for Generics? and if so how can my method figure out what object to return?
Use a base class to hold common behavior.
public abstract class Resource {
protected Resource (ResourceObject resource) {
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
}
Derived classes
public class Person : Resource {
public Person(ResourceObject resource):base(resource){
}
// Person-specific property example - Organisation
public string Organisation { get; set; }
}
public class Computer : Resource {
public Computer(ResourceObject resource) : base(resource) {
}
// Computer-specific property example - OperatingSystem
public string OperatingSystem { get; set; }
}
public class Group : Resource {
public Group(ResourceObject resource) : base(resource) {
}
// Group-specific property example - Members
public string Members { get; set; }
}
Interfaces can't be initialized and thus trying to pass a constructor argument wont work.
With the base class constraint the generic method becomes
public static T GetResource<T>(string identity) where T : Resource {
// get resource from system API
// and then return T somehow?
return (T) Activator.CreateInstance(typeof(T), resourceObject);
}
And used
Person person = GetResource<Person>("person_identity");

Get existing instance of List<T>

I've inherited a bloated project that uses a huge class as an in-memory database:
public class Database
{
public class Parameter1
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter1Value> paramValues;
}
public class Parameter2
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter2Value> paramValues;
}
public class Parameter1Value
{
public string Value { get; set;}
public Parameter parameter { get; set;}
}
public class Parameter2Value
{
public int Value { get; set;}
public Parameter2 parameter { get; set;}
}
public List<Parameter1> parameter1List { get; set; }
public List<Parameter2> parameter2List { get; set; }
}
I am creating a generic method that creates instances of Parameter1 or Parameter2 (see below) and should add those to their respective lists, but I don't know how to use those types to get the parameter1List or parameter2List instances from my Database class. The Database class holds only one List<T> property for each defined type. Is this possible?
This is the generic method used to create instances:
public static Database Add<T>(this Database database, string code, string label) where T : new()
{
T itemToCreate = (T)Activator.CreateInstance(typeof(T));
itemToCreate.Code = code;
itemToCreate.Label = label;
var listForItem = database.GetList<T>; // This is the missing functionality
listForItem.Add(itemToCreate);
return database;
}
Here is a solution using interfaces and generic constraints.
Create an interface to represent a generic parameter class and add members to the interface as required:
public interface IParameter { ... }
And an interface to represent a list of parameters:
public interface IParameterList<TParameter> where TParameter : IParameter
{
List<TParameter> ParameterList { get; set; }
}
Have the Database and Parameter classes implement these new interfaces:
public class Parameter1 : IParameter
public class Parameter2 : IParameter
public class Database : IParameterList<Parameter1>, IParameterList<Parameter2>
{
List<Parameter1> IParameterList<Parameter1>.ParameterList { get => parameter1List; set => parameter1List = value; }
List<Parameter2> IParameterList<Parameter2>.ParameterList { get => parameter2List; set => parameter2List = value; }
...
}
Add a where TParameter : IParameter constraint to your generic Parameter factory function, and have the factory function require an argument of type IParameterList<TParameter> which is an instance of the Database class. This satisfies the compiler that the Database class owns a list of TParameter. Now we just do db.ParameterList.Add(r) to add our new parameter to the correct list.
public static TParameter CreateParameter<TParameter>(IParameterList<TParameter> db) where TParameter : IParameter, new()
{
var r = new TParameter(); // This is the generic function you mentioned. Do stuff here to create your Parameter class.
db.ParameterList.Add(r); // Add the newly created parameter to the correct list
return r;
}
Code dump (full working version after I picked up your edit which added the generic factory function):
public class Parameter1 : IParameter
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter1Value> paramValues;
}
public class Parameter2 : IParameter
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter2Value> paramValues;
}
public class Parameter1Value
{
public string Value { get; set; }
public Parameter parameter { get; set; }
}
public class Parameter2Value
{
public int Value { get; set; }
public Parameter2 parameter { get; set; }
}
public class Database : IParameterList<Parameter1>, IParameterList<Parameter2>
{
// Note: Setters for the List properties probably not needed here or in IParameterList as with the following code we instantiate them at class construction time and, in this MCVE at least, there are no further assignments
public List<Parameter1> parameter1List { get; set; } = new List<Parameter1>();
public List<Parameter2> parameter2List { get; set; } = new List<Parameter2>();
List<Parameter1> IParameterList<Parameter1>.ParameterList { get => parameter1List; set => parameter1List = value; }
List<Parameter2> IParameterList<Parameter2>.ParameterList { get => parameter2List; set => parameter2List = value; }
public static TParameter Add<TParameter>(IParameterList<TParameter> db, string code, string label) where TParameter : IParameter, new()
{
var itemToCreate = new TParameter();
itemToCreate.Code = code;
itemToCreate.Label = label;
db.ParameterList.Add(itemToCreate); // Add the newly created parameter to the correct list
return itemToCreate;
}
}
public interface IParameter
{
string Code { get; set; }
string Label { get; set; }
}
public interface IParameterList<TParameter> where TParameter : IParameter
{
List<TParameter> ParameterList { get; set; }
}
// Testing:
void Main()
{
var db = new Database();
Database.Add<Parameter1>(db, "hello", "hello2");
Database.Add<Parameter1>(db, "hello", "hello2");
Database.Add<Parameter2>(db, "hello", "hello2");
Console.WriteLine($"P1 count (should be 2): {db.parameter1List.Count()}; P2 count (should be 1): {db.parameter2List.Count}");
}
Output:
P1 count (should be 2): 2; P2 count (should be 1): 1
Here is a solution which acquires the target list using generics and reflection:
public static List<T> GetList<T>(this Database dataBase) where T : new()
{
return dataBase.GetType()
.GetProperties()
.Where(x => x.PropertyType == typeof(List<T>))
.Select(x => (List<T>)x.GetValue(dataBase))
.FirstOrDefault();
}
Credit: Michael Randall in the comments

Overriding a base virtual property with a derived type is null when passing to JsonResult

I have 2 base classes which 1 for search criteria and other 1 for search results.
I also have 2 derived classes for User object versions of both of those.
When I put a breakpoint in the controller action I can see all properties are populated as I've hardcoded.
When I call this action directly in the browser, each of my derived object properties is null.
I'm guessing the JSON serialization is not able to tell the difference from the base class to the derived one.
Is there a way to solve this?
public class BaseSearchCriteria
{
public int Page { get; set; }
public int RecordsPerPage { get; set; }
}
public class BaseSearchResults
{
public int TotalResults { get; set; }
public virtual BaseSearchCriteria SearchCriteria { get; set; }
}
public class UserSearchCriteria : BaseSearchCriteria
{
public string Username { get; set; }
}
public class UserSearchResults : BaseSearchResults
{
public new UserSearchCriteria SearchCriteria { get; set; }
}
public JsonResult Search(UserSearchCriteria model)
{
var viewModel = new UserSearchResults
{
SearchCriteria = new UserSearchCriteria
{
Page = 1,
RecordsPerPage = 15
}
};
viewModel.TotalResults = 100;
return Json(viewModel, JsonRequestBehavior.AllowGet);
}
Maybe good way to deal with it is to use generics as Daniel A. White propose.
Sample gist here.

Make generic object instantiation more generic

I've got this piece of code to create new objects in a generic way:
var user = User.Create<User>(c => c.Name = "321X");
What I don't like about it is the fact I need to pass the 'generic notation' <T> for every create call. After all I create an object that I'm already referring to...
The code behind this current functionality is:
public class User : CreateBase
{
public string Name { get; set; }
}
public abstract class CreateBase
{
public DateTime CreateDate { get; set; }
public Guid Guid { get; set; }
public static T Create<T>(Action<T> init) where T : CreateBase, new()
{
T obj = new T();
obj.Guid = Guid.NewGuid();
obj.DateTime = DateTime.Now;
init(obj);
return obj;
}
}
Is it possible (and how) to refactor my code to this, to create an object?
var user = User.Create(c => c.Name = "321X");
Thanks!
Define the generic argument on the class level:
public abstract class CreateBase<T> where T : CreateBase<T> , new()
{
public static T Create(Action<T> init)
{
//...
}
}
public class User : CreateBase<User>
{
public string Name { get; set; }
}
Then you can write var user = User.Create(c => c.Name = "321X");
Otherwise the compiler cannot infer the type for your Create method without specifying the type argument.
You were not very far. Try this modification:
public abstract class CreateBase<T> where T : CreateBase<T> , new()
{
public DateTime CreateDate { get; set; }
public Guid Guid { get; set; }
public static T Create(Action<T> init)
{
T obj = new T();
obj.Guid = Guid.NewGuid();
obj.CreateDate = DateTime.Now;
init(obj);
return obj;
}
}
public class User : CreateBase<User>
{
public string Name { get; set; }
}
EDIT: Updated the code after I tested it on my local environment. It works now.
You are doing it the wrong way. Instead of getting rid of the generic argument, get rid of (needlessly) saying User.. Instead:
CreateBase.Create<User>(...)
No more redundancies.
Besides that, calling a static member of the base class through a derived class is an anti-pattern.
A better approach would be to include this functionality in the constructor of the base class (I call it ModelBase)
public abstract class ModelBase
{
public DateTime CreateDate { get; private set; }
public Guid Guid { get; private set; }
public ModelBase()
{
Guid = Guid.NewGuid();
DateTime = DateTime.Now;
}
}
public User : ModelBase
{
public User()
: base()
{
}
public User(string name)
: base()
{
Name = name
}
public string Name { get; set; }
}
Creating a user the standard way will initialize the Guid and date automatically
var user = new User { Name = "xy };
EDIT
I added a second constructor with a name parameter. I you want to force the initialization of the name, drop the first parameterless constructor.
var user = new User("xy");
If you really uncomfortable with that sintax (I, honestly, don't see much problem here)
you can do the following:
public class User : CreateBase
{
public string Name { get; set; }
public static User Create(Action<User> a)
{
return Create<User>(a); //CALL BASE CLASS GENERIC FUNCTION
}
}
After you can call it in a way you would like to do that :
var user = User.Create(c => c.Name = "321X");

Categories

Resources