One of the suggested approaches is to use Dependency Injection to inject logger to the constructor of the class. https://docs.asp.net/en/latest/fundamentals/logging.html
However, what if my class is not registered with DI container or I just want to create a static logger per a class. How can I still access LoggerFactory which was configured in Startup.cs?
This is how I would do it using nlog:
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
You could define a static property in your Startup which exposes the LoggerFactory:
public class Startup
{
public Startup(ILoggerFactory loggerFactory)
{
LoggerFactory = loggerFactory;
}
public static ILoggerFactory LoggerFactory { get; set; }
public void ConfigureServices(IServiceCollection services)
{
//..
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//..
}
}
And then access it where you need a logger:
public class MyClass
{
private static readonly ILogger _log = Startup.LoggerFactory.CreateLogger<MyClass>();
//..
}
Related
I'm using custom crudrespository to persist data in redis. However, I'm unable to autowire custom repository.
All the configuration seems correct and redis is running on my local.
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CustomRepository extends CrudRepository<String,
Long> {
String get(String key);
void put(String key, String value);
}
//////////
public class StorageServiceImpl implements IStorageService {
#Autowired
private CustomRepository respository;
#Override
public void saveParameter() {
this.respository.put("key1","value1");
}
#Override
public String getParameter() {
return this.respository.get("key1");
}
/////
#Service
public interface IStorageService {
void saveParameter();
String getParameter();
}
///////
#SpringBootApplication(scanBasePackages = {"com.example.cache"})
#EnableRedisRepositories(basePackages = {"com.example.cache.repository"})
public class ApplicationConfiguration {
public static void main(String[] args){
SpringApplication.run(ApplicationConfiguration.class, args);
new StorageServiceImpl().saveParameter();
System.out.println(new StorageServiceImpl().getParameter());
}
}
When I try running this application using gradle bootRun, I get
Exception in thread "main" java.lang.NullPointerException
at com.example.cache.impl.StorageServiceImpl.saveParameter(StorageServiceImpl.java:16)
at com.example.cache.ApplicationConfiguration.main(ApplicationConfiguration.java:17)
Not sure what's wrong?
You can't use new on any bean, you need to #Autowire it. The annotations only work with spring managed beans at every level.
Add a new bean with a a storage service and a method that makes your call after it is created.
Also, I can't remember if the spring-boot creates the bean if there is only one implementation but I believe your StorageServiceImpl needs the #Service annotation, not the interface.
Delete this from your ApplicationConfiguration class.
new StorageServiceImpl().saveParameter();
System.out.println(new StorageServiceImpl().getParameter());
Then add this new class.
#Service
public class Startup {
#Autowired
IStorageService storageService;
#PostConstruct
public void init(){
storageService.saveParameter();
System.out.println(storageService().getParameter());
}
}
And you need a config
#Configuration
#EnableRedisRepositories
public class ApplicationConfig {
#Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
return template;
}
}
In Starup file
services.AddScoped<IUserResponsitory , UserResponsitory>();
services.AddSingleton<IAuthService>(service=>new AuthServiceImpl(
service.GetService<IUserResponsitory>(),service.GetService<IConfiguration>()));
In AuthServiceImpl file:
private IUserResponsitory m_userResponsitory;
private IConfiguration m_config;
public AuthServiceImpl(IUserResponsitory userResponsitory, IConfiguration config)
{
m_config = config;
m_userResponsitory = userResponsitory;
}
In the UserResponsitory file.
public class UserResponsitory : Responsitory<Users>,IUserResponsitory
{
private DbSet<Users> userEntity;
public UserResponsitory(MyDBContext context) : base(context)
{
userEntity = context.Set<Users>();
}
}
The error below
Some of the help:
help1
help2
Can you help me.Please!
Since scope validation is applied by .NET Core, you can not resolve services with scoped lifetime (IUserResponsitory in your case) in the constructor of a service registered with singleton lifetime (IAuthService).
So you need to change your IAuthService lifetime to scoped.
services.AddScoped<IAuthService, AuthService>();
Useful Resources:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#scope-validation
https://bartwullems.blogspot.com/2019/02/aspnet-core-scope-validation.html
As the error indicates, you could not resolve a scoped service from root provider.
If you prefer use IAuthService as Singleton, you could reolve the service from IServiceProvider like
Startup.cs
services.AddScoped<IUserResponsitory , UserResponsitory>();
services.AddSingleton<IAuthService, AuthServiceImpl>();
AuthServiceImpl
public class AuthService : IAuthService
{
private readonly IServiceProvider _serviceProvider;
private readonly IConfiguration m_config;
public AuthService(IServiceProvider serviceProvider
, IConfiguration configuration)
{
_serviceProvider = serviceProvider;
m_config = configuration;
}
public void Auth()
{
using (var scope = _serviceProvider.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<IUserResponsitory>();
var result = db.Users.ToList();
}
}
}
All classes that inherit from controller have already inserted the dependencies, but I do not know how to inject those who do not inherit from contoller.
in the example below the SendEmailAndLog() method waits for the ILoggerService interface to be injected. but I do not know how to do this.
public async Task Execute(IJobExecutionContext context)
{
SendEmailHelper sendEmailHelper = new SendEmailHelper();
await sendEmailHelper.SendEmailAndLog(user, "Task", user.UserName, UserManager, loggerService);
}
this task is called the method Application_Start() on Global.asax.cs
I created a constructor to receive this service as a parameter, this is working for the methods within the controllers that call the Class Emailservice. However there are classes that do not inherit from controller so I do not know how to inject.
My Service is:
public interface ILoggerService
{
void Error(ExceptionLogger exception);
void SaveLog();
}
public class LoggerService : ILoggerService
{
private readonly ILoggerRepository loggerRepository;
private readonly IUnitOfWork unitOfWork;
public LoggerService(ILoggerRepository loggerRepository, IUnitOfWork unitOfWork)
{
this.loggerRepository = loggerRepository;
this.unitOfWork = unitOfWork;
}
public void Error(ExceptionLogger exception)
{
loggerRepository.Error(exception);
}
public void SaveLog()
{
unitOfWork.Commit();
}
}
I am using InitializingBean to initialise static properties in a modal class. This object I am auto wiring in a service
When I write a test case of service, I throws error: Failed to load ApplicationContext
Config class
public class AppConfig {
private String prop1;
protected void setProp1(String prop) {
this.prop1 = prop;
}
public String getProp1() {
return prop1;
}
}
PropertyIntilizer class
public class PropertyIntializer implements InitializingBean {
#Autowired
private AppConfig appConfig;
#Override
public void afterPropertiesSet() throws Exception {
appConfig.setProp1("PROP");
}
}
Service Class
#Service
public class Service {
#Autowired
private AppConfig appConfig;
public void doSomething(){
System.out.println(appConfig.getProp1());
}
}
TestClass
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfig.class })
public class ServiceTest {
#Autowired
private Service service;
#Test
public void testService(){
service.doSomething();
}
}
This gives an error : Failed to load ApplicationContext
But when I remove Autowired AppConfig, it works
Edit:
TestConfig class
#Configuration
#ComponentScan(basePackages = { "base.package" })
public class TestConfig {
}
my main classes are in base.package.main and test classes in base.package.test
similar issue got resolved for me by adding JRE(thats comes with application server in my case websphere) in buid path
What is the correct/recommended way of accessing the config.json file (or wherever else config is stored) in ASP.NET vNext?
In the Startup class, I set up the config like so:
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var configurationBuilder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddEnvironmentVariables();
Configuration = configurationBuilder.Build();
}
But then if I need to access the connection string elsewhere, how do I do it? For example, in the OnConfiguring of an EF context, how do I get the connection string:
protected override void OnConfiguring(EntityOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer( ??? );
I've read that you could use this:
var config = Startup.Configuration
?? new Configuration()
.AddJsonFile("config.json")
.AddEnvironmentVariables();
But (a) Startup is not static and (b) you surely don't want to go rebuilding the configuration setup every time you need it - that's duplicating code everywhere it's used.
I've also read that you should use Dependency Injection, but that link doesn't fully show you how to do it. If my DbContext constructor has an injected parameter, then how do I inject that into a parameterless BaseApiController?
This really seems like a common/simple requirement: After the configuration in Startup, how to I access that configuration elsewhere? This should in documentation/examples everywhere.
Here's what I've got that's working:
Startup
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
// create & store the configuration once
var configurationBuilder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddEnvironmentVariables();
Configuration = configurationBuilder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<Context>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<Context>()
.AddDefaultTokenProviders();
services.AddMvc();
// adding/registering the dbContext for dependency injection as a singleton
services.AddSingleton(s => new Context(Configuration));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseIdentity();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
}
}
DbContext
public sealed class Context : IdentityDbContext<IdentityUser>
{
private readonly IConfiguration _config;
public DbSet<Client> Clients { get; set; }
public Context(IConfiguration config)
{
// store the injected config
_config = config;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ForSqlServer().UseIdentity();
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(EntityOptionsBuilder optionsBuilder)
{
// use the injected config
optionsBuilder.UseSqlServer(_config.Get("Data:DefaultConnection:ConnectionString"));
base.OnConfiguring(optionsBuilder);
}
}
Controller
[Route("api/[controller]")]
public class TestController : BaseController
{
private readonly Context _context;
// have the context injected
public TestController(Context context)
{
_context = context;
}
[HttpGet]
public ActionResult Get()
{
return new ObjectResult(_context.Clients.ToList());
}
}
In the first place, you should avoid registering your database context as a singleton. Also passing around the raw IConfiguration interface isn't a good practice.
In stead could create a POCO options class:
public class DbOptions
{
public string ConnectionString { get; set }
}
And populate it in the ConfigureServices method using the section in the config.json:
services.Configure<DbOptions>(Configuration.GetConfigurationSection("Data:DefaultConnection"));
Then you can inject it into your DbContext (and in controllers, etc.):
public sealed class Context : IdentityDbContext<IdentityUser>
{
private readonly DbOptions _options;
public DbSet<Client> Clients { get; set; }
public Context(IOptions<DbOptions> optionsAccessor)
{
// store the injected options
_options = optionsAccessor.Options;
}
// other code..
}