How do I get different Configs into Custom Plugin Implementations of the same Interface? - moryx

Simplified example:
I have a custom plugin ICustomPlugin
public interface ICustomPlugin : IPlugin
{
}
and two implementations which require information from different configs. (Of course this only makes sense if the interface would define some common API but let's keep it simple)
public class CustomPluginWithConfigA : ICustomPlugin
{
}
public class CustomPluginWithConfigB: ICustomPlugin
{
}
Since I want to use both implementations my ModuleController should start both of them
[ServerModule(ModuleName)]
public class ModuleController : ServerModuleBase<ModuleConfig>
{
...
protected override void OnStart()
{
foreach (var converter in Container.ResolveAll<ICustomPlugin>())
converter.Start();
}
...
}
Now, what is the clean way to get only the config information that each implementation needs into the respective classes CustomPluginWithConfigA and CustomPluginWithConfigB?
Referencing the classes in the controller or providing the same config to all implementations feels equaly wrong to me.

You have several options and it depends whether your implementations CustomPluginA and CustomPluginB are located within the module, e.g. they are fixed components rather than flexible plugins, or loaded from additional MORYX packages.
In the first scenario you can simply add your components configuration values to the ModuleConfig and inject the config into your plugin, because a modules config is registered in its local container by default.
// In the module config
[DataMember]
public int ValueForA { get; set; }
[DataMember]
public string ValueForB { get; set; }
// In CustomPluginA: Injected
public ModuleConfig Config { get; set; }
public void SomeMethod()
{
var a = Config.ValueForA;
}
If on the other hand your plugins are fully located outside your module or can be extended with external implementations, you should use IConfiguredPlugin<TConfig> for your plugins and define a plugin base config. You will then instantiate your plugins with a factory passing their dedicated configs until we implement MORYX-Platform#10.
public class MyPluginConfig : IPluginConfig
{
[DataMember, PluginNameSelector(typeof(ICustomPlugin))]
public virtual string PluginName { get; set; }
[DataMember]
public int ValueForA { get; set; }
}
public interface ICustomPlugin : IConfiguredPlugin<MyPluginConfig>
{
}
[PluginFactory(typeof(IConfigBasedComponentSelector))]
public interface ICustomPluginFactory
{
ICustomPlugin Create(MyPluginConfig config);
}
// In your module config
[DataMember, PluginConfigs(typeof(ICustomPlugin))]
public List<MyPluginConfig> ConfiguredPlugins { get; set; }
// In your plugin
public class CustomConfigA : MyPluginConfig
{
public override PluginName { get { return nameof(CustomPluginA); } set { } }
}
[ExpectedConfig(typeof(CustomConfigA)]
[Plugin(LifeCycle.Transient, typeof(ICustomPlugin), Name = nameof(CustomPluginA))]
public class CustomPluginA : ICustomPlugin
{
public void Initialize(MyPluginConfig config)
{
var typed = (CustomPluginConfigA)config; // MORYX takes care of correct type
}
}
// In your controller Initialize
Container.LoadComponents<ICustomPlugin>(); // Load from all DLLs and packages
// In Start
var factory = Container.Resolve<ICustomPluginFactory>();
foreach (var config = Config.ConfiguredPlugins)
{
var plugin = factory.Create(config); // Calls Initialize with the config
}
Hope that answers your question.

Related

Autofac asp.net web api constructor injection works but property injection does not

I am beginner in autofac and I have to use it in new legacy project asp.net web api.
I am registering of interface and injection works fine with constructor injection.
However, the constructor is being called in numerous places directly new(), and I don't want to replace it everywhere.
So I thought about property injection, but cannot get it to work, the dependency is always null.
The app is split into multiple projects and multiple autofac modules. Autofac configuration as per docs: https://docs.autofac.org/en/latest/integration/webapi.html
I tried to make small demo app, and I was able to get property injection working using all methods from docs: https://autofac.readthedocs.io/en/latest/register/prop-method-injection.html
using Autofac;
public class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance();
builder.RegisterType<MyService>().OnActivated(e => e.Instance.MyDependency1 = e.Context.Resolve<IMyDependency>());
//builder.Register(c => new MyService { MyDependency1 = c.Resolve<IMyDependency>() });
//builder.RegisterType<MyService>().WithProperty("MyDependency1", new MyDependency()).SingleInstance();
var container = builder.Build();
container.Resolve<MyService>();
}
}
public class MyService
{
public IMyDependency MyDependency1 { get; set; }
}
public class MyDependency : IMyDependency
{
public void Hello()
{
Console.WriteLine("Hello from MyDependency1");
}
public MyDependency()
{
Hello();
}
}
public interface IMyDependency
{
public void Hello();
}
Unfortunately none of these works for my full project, the object is always null. I know it would be difficult to get help, but maybe someone can advice what to look for?
I just tried reproducing this using the WithProperty registration you have there and the test passes - I can't reproduce it, property injection is working.
If it's not working in your full project, something else is going on. Below is the totally working test I used to verify.
public class ExampleTests
{
[Fact]
public void PropertyInjection()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance();
builder.RegisterType<MyService>().WithProperty("MyDependency1", new MyDependency()).SingleInstance();
var container = builder.Build();
var svc = container.Resolve<MyService>();
Assert.NotNull(svc.MyDependency1);
}
}
public class MyService
{
public IMyDependency MyDependency1 { get; set; }
}
public class MyDependency : IMyDependency
{
public void Hello()
{
Console.WriteLine("Hello from MyDependency1");
}
public MyDependency()
{
Hello();
}
}
public interface IMyDependency
{
public void Hello();
}

ASPNET Boilerplate, extending audit log

I am trying to extend the AuditLog entity in ASPNETBOILETPLATE framework in order to add some new properties to it. I have tried to extend the AuditLog class (ExtendedAuditInfo) and implement a customised version of AuditStore Class (ExtendedAuditStore). However, I am not able to inject my new ExtendedAuditInfo in the constructor and receive two error messages regarding unmatching input parameters in the Constructor and SaveAsync method.
Class ExtendedAuditInfo:
public class ExtendedAuditInfo : AuditInfo
{
// Some properties
}
Class ExtendedAuditStore:
public class ExtendedAuditStore : AuditingStore
{
public ExtendedAuditStore(IRepository<ExtendedAuditInfo, long> auditLogRepository)
: base(auditLogRepository)
{
}
public override Task SaveAsync(ExtendedAuditInfo auditInfo)
{
if (!string.IsNullOrEmpty(auditInfo.Parameters) && auditInfo.Parameters != "{}")
{
var parameters = JsonConvert.DeserializeObject<AuditParameterInput>(auditInfo.Parameters);
if (parameters != null)
auditInfo.CustomData = parameters.Input.Id.ToString();
}
return base.SaveAsync(auditInfo);
}
}
The errors are:
cannot convert from 'Abp.Domain.Repositories.IRepository<SixB.Serafina.Auditing.ExtendedAuditInfo, long>' to 'Abp.Domain.Repositories.IRepository<Abp.Auditing.AuditLog, long>'
and
no suitable method found to override
The procedure above is based on the idea that I found Here
I found the solution based on the official document of How To Extend Existing Entities.
In order to extend the AuditLog class, inheritance must be used. Therefore a new class, let's say ExtendedAuditInfo needs to be inherited from AuditLog.
public class ExtendedAuditLog : AuditLog
{
public ExtendedAuditLog()
{
}
public ExtendedAuditLog(AuditInfo auditInfo)
{
this.BrowserInfo = auditInfo.BrowserInfo;
this.ClientIpAddress = auditInfo.ClientIpAddress;
this.ClientName = auditInfo.ClientName;
this.CustomData = auditInfo.CustomData;
this.Exception = auditInfo.Exception?.Message.ToString() + "";
this.ExecutionDuration = auditInfo.ExecutionDuration;
this.ExecutionTime = auditInfo.ExecutionTime;
this.ImpersonatorTenantId = auditInfo.ImpersonatorTenantId;
this.ImpersonatorUserId = auditInfo.ImpersonatorUserId;
this.MethodName = auditInfo.MethodName;
this.Parameters = auditInfo.Parameters;
this.ReturnValue = auditInfo.ReturnValue;
this.ServiceName = auditInfo.ServiceName;
this.TenantId = auditInfo.TenantId;
this.UserId = auditInfo.UserId;
}
//new properties
}
This class has to be added to the context and obviously, a new migration needs to be run in order to add the new properties.
public class ProjectDbContext : AbpZeroDbContext<Tenant, Role, User, ProjectDbContext >
{
/* Define a DbSet for each entity of the application */
public SerafinaDbContext(DbContextOptions<SerafinaDbContext> options)
: base(options)
{
}
public virtual DbSet<County> Counties { get; set; }
public virtual DbSet<Country> Countries { get; set; }
public virtual DbSet<Currency> Currencies { get; set; }
public virtual DbSet<OrganisationType> OrganisationTypes { get; set; }
public virtual DbSet<ExtendedAuditLog> ExtendedAuditLogs { get; set; }
}
Finally, in the ExtendedAuditStore class, IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository has to be injected as a second parameter of the constructor and can be used to insert the extended entity.
public class ExtendedAuditStore : AuditingStore
{
IRepository<ExtendedAuditLog, long> _extendedAuditLogRepository;
public ExtendedAuditStore(
IRepository<AuditLog, long> auditLogRepository,
IRepository<ExtendedAuditLog, long> extendedAuditLogRepository
)
: base(auditLogRepository)
{
_extendedAuditLogRepository = extendedAuditLogRepository;
}
public override async Task SaveAsync(AuditInfo auditInfo)
{
if (auditInfo.Exception != null)
await base.SaveAsync(auditInfo);
var auditLog = new ExtendedAuditLog(auditInfo);
//new properties can be set here
await _extendedAuditLogRepository.InsertAsync(auditLog);
}
}
Also, instead of inheriting from AuditingStore, a new implementation for IAuditingStore can be created and injected into application services.
UPDATE:
Finally, all you need is to replace the default AuditingStore in StartUp class:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddTransient<IAuditingStore, ExtendedAuditStore>();
}

Access appsettings.json in class library

I search for a solution to access the appsettings.json within a class library. I have a ASP.NET Core web application which depends on a class library. In this class library I must read some properties from the appsettings.json.
Are there any way to do that?
You should probably use Options
It's fairly simple to use. Check this article. it's well explained Src
1/ Create your Options class
namespace UsingOptions.Models
{
public class MyOptions
{
public MyOptions()
{
// Set default value.
Option1 = "value1_from_ctor";
}
public string Option1 { get; set; }
public int Option2 { get; set; } = 5;
}
}
2/ Configure it in the ConfigureServices method (In startup.cs)
public void ConfigureServices(IServiceCollection services)
{
// Adds services required for using options.
services.AddOptions();
// Register the IConfiguration instance which MyOptions binds against.
**services.Configure<MyOptions>(Configuration);**
// Add framework services.
services.AddMvc();
}
3/ Inject IOptions into your controller
public class HomeController : Controller
{
private readonly MyOptions _options;
public HomeController(IOptions<MyOptions> optionsAccessor)
{
_options = optionsAccessor.Value;
}
public IActionResult Index()
{
var option1 = _options.Option1;
var option2 = _options.Option2;
return Content($"option1 = {option1}, option2 = {option2}");
}
}

Confusion over MVC3 Code First / Repositories

Please can someone help me because I am getting confused.
I have an Entity like this:
public class Code
{
public int ID { get; set; }
public int UserID { get; set; }
public string CodeText { get; set; }
}
and an Interface like this:
public interface ICodeRepository
{
IQueryable<Code> Codes { get; }
void AddCode(Code code);
void RemoveCode(Code code);
Code GetCodeById(int id);
}
and a Repository like this:
public class SQLCodeRepository : ICodeRepository
{
private EFSQLContext context;
public SQLCodeRepository()
{
context = new EFSQLContext();
}
public IQueryable<Code> Codes
{
get { return context.Codes; }
}
public void AddCode(Code code)
{
context.Codes.Add(code);
context.SaveChanges();
}
public void RemoveCode(Code code)
{
context.Codes.Remove(code);
context.SaveChanges();
}
public Code GetCodeById(int id)
{
return context.Codes.Where(x => x.ID == id).FirstOrDefault();
}
}
and a Context like this:
public class EFSQLContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Code> Codes { get; set; }
public DbSet<PortfolioUser> PortfolioUsers { get; set; }
}
If I declare my controller like this:
public class SearchController : Controller
{
private ICodeRepository cRepo;
public SearchController(ICodeRepository codeRepository)
{
cRepo = codeRepository;
}
}
and then try to do cRepo.GetCodeById(1) nothing happens. But if I declare private ICodeRepository rep = new SQLCodeRepository and then call rep.GetCodeById(1) I can see the method in the Repository being called.
What am I doing wrong?
It looks like from the constructor signature, you are going to be doing some dependency injection. The step you are missing is to set up a DI container using a tool like Castle Windsor. You then configure the MVC resolver to use the DI container to give you the correct implementation of ICodeRepository.
See this
You'll need to create a resolver that implements IDependencyResolver and IDependencyScope and a controller factory that inheritsDefaultControllerFactory
Once you have those you can do something like the following:
MyContainer container; // this needs to be a class level member of the asax
var configuration = GlobalConfiguration.Configuration;
container = new MyContainer() // may need additional stuff here depending on DI tool used
configuration.DependencyResolver = new MyDependancyResolver(container);
var mvcControllerFactory = new MyFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(mvcControllerFactory);
You would call the above code from the asax Application_Start()
See this answer for more specifics on using Ninject and MVC3

Injecting dependencies into MVC3 filters

I've been having a heck of a time trying to get dependencies injected into a custom authorization filter.
OutletService (this is a service I'm trying to inject into my filter)
public class OutletService : IOutletService
{
#region Fields
private readonly IRepository<Outlet> _outletRepository;
#endregion
#region Ctor
public OutletService(IRepository<Outlet> outletRepository)
{
_outletRepository = outletRepository;
}
#endregion
// Rest of class omitted
CustomAuthorizeAttribute (partial, name changed for this example also)
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private IOutletService _outletService;
private IModuleService _moduleService;
public string Action { get; set; }
public int Level { get; set; }
public MarcusAuthorizeAttribute()
{
}
[Inject]
public MyAuthorizeAttribute(IOutletService outletService, IModuleService moduleService)
{
_outletService = outletService;
_moduleService = moduleService;
}
I tried using this post as an example, but as soon as I wire it up, none of my routes seem to work (IIS Express returns a 401/cannot find?)
Injecting dependencies into ASP.NET MVC 3 action filters. What's wrong with this approach?
If anyone has any ideas or suggestions, I'd appreciate it! (It's literally driving me up a wall now!)
Thanks!
Ninject's MVC extension has a mechanism for injecting dependencies into filters, which is described in the documentation here.
You may try this
Filter
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private IOutletService _outletService;
private IModuleService _moduleService;
public string Action { get; set; }
public int Level { get; set; }
public MarcusAuthorizeAttribute()
{
_outletService = DependencyResolver.Current.GetService<IHelloService>();
_moduleService = DependencyResolver.Current.GetService<IModuleService>();
}
}
Make sure you register your services with dependency resolver you are using.

Resources