C# setting property to interface class using dependency injection - c#

I have the following class that implements dependency injection for ISecurityRepository:
public class SecurityService : BaseService
{
ISecurityRepository _securityRepo = null;
public SecurityService(ISecurityRepository securityRepo)
{
_securityRepo = securityRepo;
}
}
Then I have the SecurityRepository class as follows:
public class SecurityRepository : BaseRepository, ISecurityRepository
{
public bool ValidateLogin(string userName, string password)
{
return true;
}
}
Then BaseRepository class:
public abstract class BaseRepository
{
private string _customString = null;
public string CustomString{
get {
return _customString ;
}
set
{
value = _customString ;
}
}
}
What I need is to set CustomString on BaseRepository class value from SecurityService class. Something maybe like this:
public class SecurityService : BaseService
{
ISecurityRepository _securityRepo = null;
public SecurityService(ISecurityRepository securityRepo)
{
_securityRepo = securityRepo;
// something like this or better way
_securityRepo.CustomString = "ABCD";
}
}
The idea is that within SecurityRepository class I can access CustomString value.

Related

C# How to decide which class to run in Dependency Injection based on multiple classes implement using same interface

I am working on .NET 6.0 application.
I have an Interface IDestinationFileNaming that is implemented by multiple classes. How I Can choose which class to call based on they implementing same interface. Does Iterface where T :Class plays role here?
public interface IDestinationFileNaming
{
string Generate();
}
ClassA That Implements above interface
public class ClassA : BaseFileNaming, IDestinationFileNaming
{
public ClassA()
: base() { }
public override string Generate()
{
string fileName = string.Empty;
try
{
fileName = BaseName + "AIM_UBW";
}
catch (Exception ex)
{ }
return fileName;
}
I
ClassB That Implements above interface
public class ClassB : BaseFileNaming, IDestinationFileNaming
{
public ClassB()
: base() { }
public override string Generate()
{
string fileName = string.Empty;
try
{
fileName = BaseName + "DDS_UBW";
}
catch (Exception ex)
{ }
return fileName;
}
}
I have register dependencies in DI Container as
services.AddScoped<IDestinationFileNaming, ClassA>();
services.AddScoped<IDestinationFileNaming, ClassB>();
ClassC
I want to run ClassA here...
public class ClassC{
private readonly IDestinationFileNaming _destinationFileNaming;
public ClassC(IDestinationFileNaming destinationFileNaming)
:base()
{
this._destinationFileNaming = destinationFileNaming;
}
public void Run(){
// Hot to call ClassA from using above Interface?
}
}
I have found two approaches
Approach 1
register dependencies
services.AddScoped<IDestinationFileNaming, ClassA>();
services.AddScoped<IDestinationFileNaming, ClassB>();
Class C
public class ClassC{
private readonly IEnumerable<IDestinationFileNaming> _destinationFileNaming;
public ClassC(IEnumerable<IDestinationFileNaming> destinationFileNaming)
:base()
{
this._destinationFileNaming = destinationFileNaming;
}
public void Run(){
}
private dynamic ResolveObject()
{
var x = _destinationFileNamings.SingleOrDefault(_ => _.GetType() == typeof(ClassA));
var y = x.Generate();
var a = _destinationFileNamings.SingleOrDefault(_ => _.GetType() == typeof(ClassB));
var b = a.Generate();
return x;
}
}
Approach 2
interface
public interface IDestinationFileNaming<T> where T : class
{
string Generate();
}
register in DI
services.AddScoped<IDestinationFileNaming<ClassA>, ClassA>();
services.AddScoped<IDestinationFileNaming<ClassB>, ClassB>();
Class C
public class ClassC{
private readonly IDestinationFileNaming<ClassA> _destinationFileNaming;
public ClassC(IDestinationFileNaming<ClassA> destinationFileNaming)
:base()
{
this._destinationFileNaming = destinationFileNaming;
}
public void Run(){
x = _destinationFileNaming.Generate();
}
}

linq2db, using DataConnection as abstract class

I am trying to use the DataConnection class of linq2db in an abstract base class like so:
public abstract class BaseDbDao<T> : DataConnection where T : IDatabase
{
public string DBName => DBContext.Database;
public T DBContext { get; }
public DataConnection GetDataConnection()
{
return (this);
}
internal BaseDbDao(RelativityHelper helper, int workspaceId)
: base(GetDataProvider(), helper.GetDBContextNew(workspaceId).GetConnection())
{
DBContext = (T)helper.GetDBContextNew(workspaceId);
}
public static IDataProvider GetDataProvider()
{
LinqToDB.Common.Configuration.AvoidSpecificDataProviderAPI = true;
return new SqlServerDataProvider("", SqlServerVersion.v2017);
}
}
I am then calling it up from another class like so:
public class EdMeDBDao : BaseDbDao<MSSQLDBContext>
{
public ITable<EdMeTableRow> EdMeDatabase => GetTable<EdMeTableRow>();
public EdMeDBDao(RelativityHelper helper)
: base(helper, "123456")
{
}
public List<RelativityWorkspace> GetActiveProjects()
{
using (var db = GetDataConnection())
{
var a = GetTable<EdMeTableRow>().CaseName
};
}
}
However, I can't seem to call var a = GetTable<EdMeTableRow>().CaseName as ITable<EdMeTableRow> does not contain a definition for 'CaseName'
EdMeTableRow is defined as:
[Table(Name = "NuixWorkflowCases", Schema = "dbo")]
public class EdMeTableRow
{
public string CaseName { get; set; }
}
How can I access EdMeTableRow class from the EdMeDBDao class?

Return Yield for interface generic classes

I am struggling with how to return Yield for type that is interface type of IBasic. Currently i can have three diffrent types of IBasic: InputData1, InputData2, InputData3.
The problem is on this part of code:
internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic
{
private readonly ICsvSettings _settings;
public CsvRepo(ICsvSettings settings)
{
_settings = settings;
}
public IEnumerable<T> GetRecords()
{
//return from line in File.ReadLines(_settings.Path)
// select line.Split(',') into parts
// where parts.Length == 3
// select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
}
}
in the line: select new InputData
i am going to say something like return new IBasic but diffrent InputDataX has diffrent parameters and i am not sure how to do so? Is it possible?
This is full code:
namespace ClassLibrary3
{
public interface IRepo { }
public interface IRepository<T> : IRepo where T : IBasic { }
public interface ICsvRepo<T> : IRepository<T> where T : IBasic
{
IEnumerable<T> GetRecords();
}
public interface ISqlRepo
{
}
public interface IOracleRepo<T> : IRepository<T> where T : IBasic { }
public interface IRepoX : IRepo { }
public interface ICsvSettings
{
string Path { get; }
string FileName { get; }
}
public interface ISqlSettings
{
string ConnectionString { get; }
string Username { get; }
string Password { get; }
}
internal class CsvSettings : ICsvSettings
{
public string Path { get; set; }
public string FileName { get; set; }
}
internal class SqlSettings : ISqlSettings
{
public string ConnectionString { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic
{
private readonly ICsvSettings _settings;
public CsvRepo(ICsvSettings settings)
{
_settings = settings;
}
public IEnumerable<T> GetRecords()
{
return null;
//return from line in File.ReadLines(_settings.Path)
// select line.Split(',') into parts
// where parts.Length == 3
// select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
}
}
internal class SqlRepo : ISqlRepo
{
private readonly ISqlSettings _settings;
private readonly IRepoX _repoX;
public SqlRepo(ISqlSettings settings, IRepoX repoX)
{
_settings = settings;
_repoX = repoX;
}
}
internal class OracleRepo<T> : IOracleRepo<T> where T : IBasic
{
private readonly ISqlSettings _settings;
private readonly IRepoX _repoX;
public OracleRepo(ISqlSettings settings, IRepoX repoX)
{
_settings = settings;
_repoX = repoX;
}
}
internal class OracleRepo333<T> : IOracleRepo<T> where T : IBasic
{
private readonly ISqlSettings _settings;
private readonly IRepoX _repoX;
public int id;
public OracleRepo333(ISqlSettings settings, IRepoX repoX)
{
_settings = settings;
_repoX = repoX;
}
}
internal class RepoX : IRepoX { }
public class RepoModule : NinjectModule
{
private readonly string _username;
private readonly string _password;
public RepoModule(string username, string password)
{
_username = username;
_password = password;
}
public override void Load()
{
Bind<ICsvSettings>().ToConstant(new CsvSettings
{
FileName = "foo",
Path = Config.Instance.ServerName,
}).InSingletonScope();
Bind<ISqlSettings>().ToConstant(new SqlSettings
{
ConnectionString = "foo",
Password = _password,
Username = _username
}).InSingletonScope();
Bind<IRepoX>().To<RepoX>();
Bind(typeof(ICsvRepo<>)).To(typeof(CsvRepo<>));
Bind(typeof(ISqlRepo)).To(typeof(SqlRepo));
Bind(typeof(IOracleRepo<>)).To(typeof(OracleRepo<>));
Bind(typeof(IOracleRepo<>)).To(typeof(OracleRepo333<>));
}
}
public interface IBasic
{
}
public class InputData1 : IBasic
{
public int X;
public int Y;
}
public class InputData2 : IBasic
{
public string Name;
}
public class InputData3 : IBasic
{
public IEnumerable<string> WhateverList;
}
}
class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel(new RepoModule("foo", "bar")); /*some other modules here maybe?*/
//thousand of code lines later...
var csvRepo = kernel.Get<ICsvRepo<InputData1>>();
//var data = FetchData(csvRepo);
var sqlRepo = kernel.Get<ISqlRepo>();
//data = FetchData(sqlRepo);
// var oracleRepo = kernel.Get<IOracleRepo<InputData>>();
//data = FetchData(oracleRepo);
var oracleRepos = kernel.GetAll<List<IOracleRepo<InputData1>>>();}
}
}
//static T[] FetchData<T>(IRepository<InputData> repo)
//{
// throw new NotImplementedException();
//}
}
The problem is that you are trying to return a concrete type where a generic type is expected. Consider the following instantiation of CsvRepo<T>
var repo = new CsvRepo<InputData1Derived>(null);
repo.GetRecords().First().PropFromInputData1Derived
You are instantiating InputData while the caller expects InputDataDerived. This is why the compiler does not let you do this.
You could have several solutions, let CsvRepo could be abstract and implement it for specific classes:
internal abstract class CsvRepo<T> : ICsvRepo<T> where T : IBasic
{
public CsvRepo()
{
}
public abstract IEnumerable<T> GetRecords();
}
internal class InputDataCsvRepo : CsvRepo<InputData1>
{
public override IEnumerable<InputData1> GetRecords()
{
return from line in File.ReadLines(_settings.Path)
select line.Split(',') into parts
where parts.Length == 3
select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
}
}
Or you can make the T parameter have a default constructor and use that (but only properties in IBasic will be initializable which is not what you want probably.
This seems to be one of those situations where if you have a hammer everything looks like a nail. In short there is no need to make the argument for the method generic as already know the concrete return type for any implementation and you want that return type to implement a specific interface.
Remove the generic type constraint T on method GetRecords and replace it with IBasic interface constraint.
public interface ICsvRepo<T> : IRepository<T> where T : IBasic
{
IEnumerable<IBasic> GetRecords();
}
internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic
{
private readonly ICsvSettings _settings;
public CsvRepo(ICsvSettings settings)
{
_settings = settings;
}
public IEnumerable<IBasic> GetRecords()
{
return from line in File.ReadLines(_settings.Path)
select line.Split(',') into parts
where parts.Length == 3
select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
}
}
I altered your code below to only include what is needed to show the solution.
On an unrelated note making fields public is almost always a bad idea because you expose the internals of the class. Make them into properties instead with public getter/setters.
Example:
public class InputData : IBasic
{
public int X {get;set;}
public int Y {get;set;}
}

Perform initialization first in Ninject

I have an interface like this
public interface IPerson { }
And implementations
public class Fireman : IPerson
{
public string Name { get; set; }
public bool WithAssignedTruck { get; set; }
...
}
public class Pilot : IPerson
{
public string Name { get; set; }
public int Age { get; set; }
...
}
And pass them to a constructor
public class Registration : IRegistration
{
private readonly Fireman _fireman;
private readonly Pilot _pilot;
public Registration(Pilot pilot, Fireman fireman)
{
this._fireman = fireman;
this._pilot = pilot;
}
}
And here's what the initialization method looks like.
public T PopulateProfile<T>() where T : IPerson, new()
{
var personProfile = Activator.CreateInstance<T>();
...
return personProfile;
}
Please take note that this code is just an example.
I have a method that will set the value of each property of these classes which are from database. What I need to do is that, when I ask Ninject for any class that implements IPerson interface, Ninject should execute the method first, thus, Ninject will return an initialized class. Hope you could give me a hand. Thank you.
You can use Ninject.Extensions.Conventions in combination with an IBindingGenerator which generates a ToMethod binding:
BindingGenerator
internal class PersonBindingGenerator : IBindingGenerator
{
private static readonly MethodInfo PopulateOpenGenericMethodInfo =
typeof(IProfileService).GetMethod("PopulateProfile");
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(
Type type,
IBindingRoot bindingRoot)
{
yield return bindingRoot
.Bind(type)
.ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
}
private static object CreatePerson(
IProfileService profileService,
Type type)
{
var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
return closedGeneric.Invoke(profileService, new object[0]);
}
}
Bindings
kernel.Bind<IProfileService>().To<ProfileService>();
kernel.Bind(s => s
.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom<IPerson>()
.BindWith<PersonBindingGenerator>());
Test
Complete Test code for reference.
using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Conventions.BindingGenerators;
using Ninject.Syntax;
using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;
namespace NinjectTest.SO36424126
{
public interface IPerson
{
string SomeValue { get; set; }
}
class BarPerson : IPerson
{
public string SomeValue { get; set; }
}
class FooPerson : IPerson
{
public string SomeValue { get; set; }
}
public interface IProfileService
{
T PopulateProfile<T>()
where T : IPerson, new();
}
internal class ProfileService : IProfileService
{
public T PopulateProfile<T>()
where T : IPerson, new()
{
var personProfile = Activator.CreateInstance<T>();
personProfile.SomeValue = "initialized";
return personProfile;
}
}
internal class PersonBindingGenerator : IBindingGenerator
{
private static readonly MethodInfo PopulateOpenGenericMethodInfo = typeof(IProfileService).GetMethod("PopulateProfile");
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
yield return bindingRoot
.Bind(type)
.ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
}
private static object CreatePerson(IProfileService profileService, Type type)
{
var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
return closedGeneric.Invoke(profileService, new object[0]);
}
}
public class Test
{
[Fact]
public void Foo()
{
var kernel = new StandardKernel();
kernel.Bind<IProfileService>().To<ProfileService>();
kernel.Bind(s => s
.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom<IPerson>()
.BindWith<PersonBindingGenerator>());
kernel.Get<BarPerson>().SomeValue.Should().Be("initialized");
}
}
}

generic type of class property

I have the following classes:
BaseField:
public abstract class BaseField {
...
public BaseField()
{
}
public BaseField(E_FieldType fieldType)
{
_FieldType = fieldType;
}
}
TextField:
public class TextField : BaseField {
...
public TextField() : base(E_FieldType.Text)
{
}
}
DateField:
public class DateField : BaseField {
...
public DateField() : base(E_FieldType.Date)
{
}
}
And DataBlock class which should contain TextField or DateField:
public class DataBlock<T> : BaseBlock where T : BaseField, new() {
...
private T _Field;
public DataBlock(string name): base(name, E_BlockType.Data)
{
_Field = new T();
}
}
The following line works fine:
DataBlock<TextField> db = new DataBlock<TextField>("qwe");
But It is not possible to write this code:
public ObservableCollection<DataBlock<BaseField>> DataBlockList { get; set; }
public DataBlockViewModel()
{
DataBlockList = new ObservableCollection<DataBlock<BaseField>>();
DataBlockList.Add(new DataBlock<TextField>("qwe"));
DataBlockList.Add(new DataBlock<DateField>("asd"));
}
The error is:
'BaseField' must be a non-abstract type with a public parameterless
constructor in order to use it as parameter 'T' in the generic type or
method 'DataBlock<T>'
Could you please advise how to solve the issue that I can create ObservableCollection<DataBlock<BaseField>> collection?
I can remove new() from public class DataBlock<T> : BaseBlock where T : BaseField and
public DataBlock(string name): base(name, E_BlockType.Data)
{
//_Field = new T();
}
In this case I can create DataBlockList = new ObservableCollection<DataBlock<BaseField>>();
but it is not possible to write:
DataBlockList.Add(new DataBlock<TextField>("qwe"));
DataBlockList.Add(new DataBlock<DateField>("asd"));
There are 2 ways to get rid of the error:
1) You can make the class BaseField non-abstract
public abstract class BaseField {
...
public BaseField()
{
}
public BaseField(E_FieldType fieldType)
{
_FieldType = fieldType;
}
}
2) Pass the new BaseField object as constructor parameter to DataBlock and remove the new() constraint.
public class DataBlock<T> : BaseBlock where T : BaseField {
...
private T _Field;
public DataBlock(string name, T field): base(name, E_BlockType.Data)
{
_Field = field;
}
}

Categories

Resources