Sorry this is a bit new to me so I don't quite 'get it'.
I already have a logging provider
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder =>
{
var loggingSection = Configuration.GetSection("Logging");
loggingBuilder.AddFile(loggingSection);
loggingBuilder.AddConsole();
loggingBuilder.AddDebug();
I am using the package NReco.Logging.File to define AddFile etc.
I want to make it so that exceptions are emailed to me too. So I followed https://learn.microsoft.com/en-us/dotnet/core/extensions/custom-logging-provider to create a custom logger.
public sealed class EmailLoggerConfiguration
{
public int EventId { get; set; }
public string EmailToSendTo { get; set; }
public IEmailSender EmailSender { get; set; }
}
internal class EmailLoggingProvider : ILoggerProvider
{
private readonly IDisposable? _onChangeToken;
private EmailLoggerConfiguration _currentConfig;
private readonly ConcurrentDictionary<string, EmailLogger> _loggers =
new(StringComparer.OrdinalIgnoreCase);
private readonly IEmailSender emailSender;
public EmailLoggingProvider(
IOptionsMonitor<EmailLoggerConfiguration> config)
{
_currentConfig = config.CurrentValue;
_onChangeToken = config.OnChange(updatedConfig => _currentConfig = updatedConfig);
}
public ILogger CreateLogger(string categoryName) =>
_loggers.GetOrAdd(categoryName, name => new EmailLogger(name, GetCurrentConfig ));
private EmailLoggerConfiguration GetCurrentConfig() => _currentConfig;
public void Dispose()
{
_loggers.Clear();
_onChangeToken?.Dispose();
}
}
internal class EmailLogger : ILogger
{
private readonly string categoryName;
private Func<EmailLoggerConfiguration> getCurrentConfig;
IEmailSender emailSender;
public EmailLogger(string categoryName, Func<EmailLoggerConfiguration> getCurrentConfig)
{
this.getCurrentConfig = getCurrentConfig;
this.categoryName = categoryName;
}
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!;
public bool IsEnabled(LogLevel logLevel) => !String.IsNullOrEmpty(getCurrentConfig().EmailToSendTo);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var emailTo = getCurrentConfig().EmailToSendTo;
//var emailServer = getCurrentConfig().EmailSender;
if (!String.IsNullOrEmpty(emailTo) && exception != null)
{
emailSender.SendEmailAsync(emailTo, "Admin exception", exception.ToString());
}
}
}
public static class EmailLoggingExtensions
{
public static ILoggingBuilder AddEmailLogger(
this ILoggingBuilder builder)
{
builder.AddConfiguration();
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, EmailLoggingProvider>());
LoggerProviderOptions.RegisterProviderOptions<EmailLoggerConfiguration, EmailLoggingProvider>(builder.Services);
return builder;
}
public static ILoggingBuilder AddEmailLogger(
this ILoggingBuilder builder,
Action<EmailLoggerConfiguration> configure)
{
builder.AddEmailLogger();
builder.Services.Configure(configure);
return builder;
}
}
You can see that EmailLogger.Log requires emailSender which should be an IEmailSender but I cannot figure out how to get it there using DI.
I realise that you can chain dependencies in DI but ???? I don't see how in this context.
I tried this
loggingBuilder.AddEmailLogger(c =>
{
c.EmailToSendTo = Configuration["Logging:Email:EmailToSendTo"];
c.EmailSender = new AuthMessageSender(????, Configuration);
});
but that didn't help and wouldn't even be right anyway.
In fact, by default, EmailSender is the implementation method of IEmailSender, which is used to call the SendEmailAsync() method. You don't need to go and set c.EmailSender = xxx.
You can consider the following dependency injection approach:
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string message);
}
public class EmailSender : IEmailSender
{
//...
private readonly ILogger<EmailSender> logger;
public EmailSender(ILogger<EmailSender> logger) {
//...
this.logger = logger;
}
public Task SendEmailAsync(string email, string subject, string message) {
//...
}
}
At this point, IEmailSender will exist as a custom interface instead of inheriting from Microsoft.AspNetCore.Identity.UI.Services.
And you need to register it as a service:
services.AddTransient<IEmailSender, EmailSender>();
Helpful links:
Add ILogger to send email service
Should I use IEmailSender?
Using IEmailSender from Configure() in my Startup.cs file
Hope this will help you better understand IEmailSender and dependency injection.
I am moving code from .net 4.6.1 to .net 6 API. There is a lot of code using: ConfigurationManager.AppSettings["something"].ToString()
to read App.config
In .net6 we use appsettings.json and read it with dependency injection
for example:
private readonly IOptions<Configs> _configs;
public WeatherForecastController(IOptions<Configs> configs)
{
_configs = configs;
}
public IEnumerable<WeatherForecast> Get()
{
var valFromConfig = _configs.Value.Val;
}
How can read settings without passing _configs as parameter of the method?
public IEnumerable<WeatherForecast> Get()
{
var ret = MyClass.GetConfigVal();
}
where MyClass in old solution:
public class MyClass
{
public static string GetConfigVal()
{
return ConfigurationManager.AppSettings["something"].ToString(); // How to change this
}
}
in .net 6 ,You could inject IConfigration into your controller:
private readonly IConfiguration _configuration;
public WeatherForecastController(IConfiguration configuration)
{
_configuration = configuration;
}
public IEnumerable<WeatherForecast> Get()
{
var valFromConfig = _configuration.GetSection(".....").Value;
}
For more details,you could check this document:
So I create classes for setting
public static class ApplicationSettings
{
public const string options = "Appsettings";
public static AppSettings AppSettings { get; private set; } = new AppSettings();
}
public class AppSettings
{
public string SomeSetting { get; set; } = string.Empty;
}
Appsetting.json look like
{
"AppSettings": {
"SomeSetting": "some text"
}
}
Add this to program.cs
ConfigurationManager configuration = builder.Configuration;
configuration.GetSection(ApplicationSettings.options).Bind(ApplicationSettings.AppSettings);
So I can use it everywhere
protected static readonly AppSettings AppSettings = ApplicationSettings.AppSettings;
var mySetting = AppSettings.SomeSetting
I am really stuck and need your help.
I have a .NET Core 2.1 API and I want to create a nUnitTest project (NUnit 3.10.1) to test the controller of the API but I don't know how to call the controller's action method from the nUnitTest and really need some help.
Things I have tried:
1) Generate parameterless constructor in the controller and instantiate controller from the Test method but that doesn't work.
2) Generate a constructor in the Test class to define the db context but that also doesn't work.
Here is my API Controller:
public class PersonController : ControllerBase
{
private readonly NetCoreAPI1Context _context;
public PersonController(NetCoreAPI1Context context)
{
_context = context;
}
// POST: api/Person
[HttpPost]
public async Task<IActionResult> PostPerson([FromBody] Person person)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Persons.Add(person);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPerson", new { id = person.Id }, person);
}
}
Here is NetCoreAPI1Context class
public class NetCoreAPI1Context : DbContext
{
public NetCoreAPI1Context(DbContextOptions<NetCoreAPI1Context> options)
: base(options)
{
}
public DbSet<Person> Persons { get; set; }
}
Here is Person class
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
I've verified that the PostPerson action in the controller worked.
I then add a nUnitTest project to the solution.
Here is my simple test class and method:
class PersonControllerTest
{
Person _person;
//NetCoreAPI1Context _context;
[SetUp]
public void Setup()
{
_person = new Person
{
FirstName = "David",
LastName = "Johnson",
Email = "dj#dj.com"
};
}
//public PersonControllerTest(NetCoreAPI1Context context)
//{
// _context = context;
//}
[Test]
public void PostPersonTest()
{
var person = _person; // confirm that "person" has data and ready to be used
//
// HOW To CALL THE COTROLLER METHOD PostPerson(...) HERE????
//
}
}
Here is the step about how to NUnit test:
1.In your nUnitTest project,you need to right-click Dependences and choose add reference(Web Api project):
2.WebApi Project Controller:
[Route("api/[controller]")]
[ApiController]
public class PeopleController : ControllerBase
{
private readonly WebApi2Context _context;
public PeopleController(WebApi2Context context)
{
_context = context;
}
// GET: api/People
[HttpGet]
public async Task<ActionResult<IEnumerable<Person>>> GetPerson()
{
return await _context.Person.ToListAsync();
}
// POST: api/People
[HttpPost]
public async Task<ActionResult> PostPerson(Person person)
{
_context.Person.Add(person);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPerson", new { id = person.Id }, person);
}
}
3.nUnitTest project(Be sure that you have installed the package Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer):
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NUnit.Framework;
using WebApi2.Controllers;
using WebApi2.Data;
using WebApi2.Models;
namespace NUnitTestProject1
{
class PersonControllerTest
{
public static DbContextOptions<WebApi2Context> dbContextOptions { get; }
public static string connectionString = "Server=(localdb)\\mssqllocaldb;Database=YourDatabaseName;Trusted_Connection=True;MultipleActiveResultSets=true";
Person _person;
static PersonControllerTest()
{
dbContextOptions = new DbContextOptionsBuilder<WebApi2Context>()
.UseSqlServer(connectionString)
.Options;
}
[SetUp]
public void Setup()
{
_person = new Person
{
//Id=1,If Id is not designed by using IDENTITY (1, 1),you need to add this line
FirstName = "David",
LastName = "Johnson",
Email = "dj#dj.com"
};
}
[Test]
public void Test1()
{
var context = new WebApi2Context(dbContextOptions);
PeopleController person = new PeopleController(context);
var data = person.PostPerson(_person);
var response = data.Result as CreatedAtActionResult;
var item = response.Value as Person;
Assert.AreEqual("David", item.FirstName);
Assert.AreEqual("Johnson", item.LastName);
Assert.AreEqual("dj#dj.com", item.Email);
}
}
}
4.Result:
I am using AutoMapper 6.2.0 in my ASP.NET MVC 5 application.
When I call my view through controller it shows all things right. But, when I refresh that view, Visual Studio shows an error:
System.InvalidOperationException: 'Mapper already initialized. You must call Initialize once per application domain/process.'
I am using AutoMapper only in one controller. Not made any configuration in any place yet nor used AutoMapper in any other service or controller.
My controller:
public class StudentsController : Controller
{
private DataContext db = new DataContext();
// GET: Students
public ActionResult Index([Form] QueryOptions queryOptions)
{
var students = db.Students.Include(s => s.Father);
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Student, StudentViewModel>();
});
return View(new ResulList<StudentViewModel> {
QueryOptions = queryOptions,
Model = AutoMapper.Mapper.Map<List<Student>,List<StudentViewModel>>(students.ToList())
});
}
// Other Methods are deleted for ease...
Error within controller:
My Model class:
public class Student
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string CNIC { get; set; }
public string FormNo { get; set; }
public string PreviousEducaton { get; set; }
public string DOB { get; set; }
public int AdmissionYear { get; set; }
public virtual Father Father { get; set; }
public virtual Sarparast Sarparast { get; set; }
public virtual Zamin Zamin { get; set; }
public virtual ICollection<MulaqatiMehram> MulaqatiMehram { get; set; }
public virtual ICollection<Result> Results { get; set; }
}
My ViewModel Class:
public class StudentViewModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string CNIC { get; set; }
public string FormNo { get; set; }
public string PreviousEducaton { get; set; }
public string DOB { get; set; }
public int AdmissionYear { get; set; }
public virtual FatherViewModel Father { get; set; }
public virtual SarparastViewModel Sarparast { get; set; }
public virtual ZaminViewModel Zamin { get; set; }
}
If you want/need to stick with the static implementation in a unit testing scenario, note that you can call AutoMapper.Mapper.Reset() before calling initialize. Do note that this should not be used in production code as noted in the documentation.
Source: AutoMapper documentation.
When you refresh the view you are creating a new instance of the StudentsController -- and therefore reinitializing your Mapper -- resulting in the error message "Mapper already initialized".
From the Getting Started Guide
Where do I configure AutoMapper?
If you're using the static Mapper method, configuration should only happen once per AppDomain. That means the best place to put the configuration code is in application startup, such as the Global.asax file for ASP.NET applications.
One way to set this up is to place all of your mapping configurations into a static method.
App_Start/AutoMapperConfig.cs:
public class AutoMapperConfig
{
public static void Initialize()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Student, StudentViewModel>();
...
});
}
}
Then call this method in the Global.asax.cs
protected void Application_Start()
{
App_Start.AutoMapperConfig.Initialize();
}
Now you can (re)use it in your controller actions.
public class StudentsController : Controller
{
public ActionResult Index(int id)
{
var query = db.Students.Where(...);
var students = AutoMapper.Mapper.Map<List<StudentViewModel>>(query.ToList());
return View(students);
}
}
I've used this method before and it worked till version 6.1.1
Mapper.Initialize(cfg => cfg.CreateMap<ContactModel, ContactModel>()
.ConstructUsing(x => new ContactModel(LoggingDelegate))
.ForMember(x => x.EntityReference, opt => opt.Ignore())
);
Since version 6.2, this doesn't work any more. To correctly use Automapper create a new Mapper and us this one like this:
var mapper = new MapperConfiguration(cfg => cfg.CreateMap<ContactModel, ContactModel>()
.ConstructUsing(x => new ContactModel(LoggingDelegate))
.ForMember(x => x.EntityReference, opt => opt.Ignore())).CreateMapper();
var model = mapper.Map<ContactModel>(this);
In case you really need to "re-initialize" AutoMapper you should switch to the instance based API to avoid System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process.
For example, when you are creating the TestServer for xUnit tests you can just set ServiceCollectionExtensions.UseStaticRegistration inside fixure class constructor to false to make the trick:
public TestServerFixture()
{
ServiceCollectionExtensions.UseStaticRegistration = false; // <-- HERE
var hostBuilder = new WebHostBuilder()
.UseEnvironment("Testing")
.UseStartup<Startup>();
Server = new TestServer(hostBuilder);
Client = Server.CreateClient();
}
For Unit Testing, you can add Mapper.Reset() to your unit test class
[TearDown]
public void TearDown()
{
Mapper.Reset();
}
You can use automapper as Static API and Instance API ,
Mapper already initialized is common issue in Static API , you can use mapper.Reset()
where you initialized mapper but this this not an answer at all.
Just try with instance API
var students = db.Students.Include(s => s.Father);
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Student, StudentViewModel>();
});
IMapper iMapper = config.CreateMapper();
return iMapper.Map<List<Student>, List<StudentViewModel>>(students);
Automapper 8.0.0 version
AutoMapper.Mapper.Reset();
Mapper.Initialize(
cfg => {
cfg.CreateMap<sourceModel,targetModel>();
}
);
You can simply use Mapper.Reset().
Example:
public static TDestination MapToObject<TSource, TDestination>(TSource Obj)
{
Mapper.Initialize(cfg => cfg.CreateMap<TSource, TDestination>());
TDestination tDestination = Mapper.Map<TDestination>(Obj);
Mapper.Reset();
return tDestination;
}
If you are using MsTest you can use the AssemblyInitialize attribute so that mapping gets configured only once for that assembly (here test assembly). This is generally added into to the base class of controller unit tests.
[TestClass]
public class BaseUnitTest
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.EmailAddress));
});
}
}
I hope this answer helps
If you are using Mapper in UnitTest and your tests more then one, You may use Mapper.Reset()
`
//Your mapping.
public static void Initialize()
{
Mapper.Reset();
Mapper.Initialize(cfg =>
{
cfg.CreateMap<***>
}
//Your test classes.
[TestInitialize()]
public void Initialize()
{
AutoMapping.Initialize();
}`
private static bool _mapperIsInitialized = false;
public InventoryController()
{
if (!_mapperIsInitialized)
{
_mapperIsInitialized = true;
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Inventory, Inventory>()
.ForMember(x => x.Orders, opt => opt.Ignore());
}
);
}
}
I am using ASP.NET EntityFramework MVC (All Latest) with Visual Studio 2013.
I am attempting to come up with a unifying means of standardizing the abstraction between my object and data layer.
I know that many people like to use the IRepository pattern with MVC. My biggest issue with this is that it forces you to create and maintain a second object type (The Repository) for each of the objects that you want to maintain. My solution has been to combing the IRepository method with a Static Factory pattern to make a Static Repository, for instance:
public class SiteDatabase : DbContext
{
// Singleton
private static SiteDatabase _Instance;
public static SiteDatabase Instance
{
get
{
if (_Instance == null)
{
_Instance = new SiteDatabase();
}
return _Instance;
}
}
public DbSet<User> Users { get; set; }
}
public class User : IUser
{
public static User Create(string UserName)
{
User item = new User();
item.UserName = UserName;
SiteDatabase.Instance.Users.Add(item);
return item;
}
public static User Find(string UserName)
{
return SiteDatabase.Instance.Users.SingleOrDefault(x => x.UserName == UserName);
}
public static User[] All()
{
return SiteDatabase.Instance.Users.ToArray();
}
public string Id { get; set; }
public string UserName { get; set; }
protected User()
{
Id = Guid.NewGuid().ToString();
}
public void Delete()
{
SiteDatabase.Instance.Users.Remove(this);
}
}
public class Page
{
public static Page Create(string PageName)
{
...
}
public static Page Find(string PageName)
{
...
}
public static Page[] All()
{
...
}
...
public void Delete()
{
...
}
}
My question is: will this pattern cause me to miss out on any built-in functionality that the normal repository pattern would allow me to capture?
The non-static methods could be handled with an interface, but what about the static methods? Is there any way to have a base class that ensures that static methods will exist in derived classes?