I'm trying to create a integration test using Test Serer.
But I don't know why, I'm always getting an error message [404 not found]. I'm trying the simplest route of "/".
If someone have some idea of how to fix it, I would be very grateful.
Here is the code I use in my Test Server. This is my base class:
namespace NSG.Integration.Helpers
{
public class UnitTestFixture
{
//
public SqliteConnection sqliteConnection;
public ApplicationDbContext db_context;
public UserManager<ApplicationUser> userManager;
public RoleManager<ApplicationRole> roleManager;
public IConfiguration configuration = null;
private IWebHostBuilder _builder;
private TestServer _server;
private HttpClient _client;
//
public TestServer server { get { return _server; } }
public HttpClient client { get { return _client; } }
//
public void Fixture_ControllerTestSetup()
{
string projectPath = #"C:\Dat\Nsg\L\Web\22\Net.Incident4\NSG.NetIncident4.Core";
IWebHostBuilder _builder = null;
_server = null;
_client = null;
_builder = new WebHostBuilder()
.UseContentRoot(projectPath)
.UseEnvironment("Development")
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(projectPath);
config.AddJsonFile("appsettings.json");
}).UseStartup<TestStartup>();
_server = new TestServer(_builder);
_client = _server.CreateClient();
userManager = _server.Services.GetService<UserManager<ApplicationUser>>();
roleManager = _server.Services.GetService<RoleManager<ApplicationRole>>();
db_context = _server.Services.GetService<ApplicationDbContext>();
}
[TearDown]
public void Fixture_TearDown()
{
Console.WriteLine("Fixture_UnitTestSetup: Dispose ...");
if ( sqliteConnection != null )
{
sqliteConnection.Close();
sqliteConnection.Dispose();
sqliteConnection = null;
}
if (db_context != null)
{
db_context.Database.EnsureDeleted();
db_context.Dispose();
db_context = null;
}
if (userManager != null)
{
userManager.Dispose();
userManager = null;
}
if (roleManager != null)
{
roleManager.Dispose();
roleManager = null;
}
if (_client != null)
{
_client.Dispose();
_client = null;
}
if (_server != null)
{
_server.Dispose();
_server = null;
}
}
}
}
This is test startup class. I'm using in-memory SQLite as my DB:
namespace NSG.Integration.Helpers
{
public class TestStartup : NSG.NetIncident4.Core.Startup
{
//
public TestStartup(IConfiguration configuration) : base(configuration)
{
}
//
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
//
ApplicationDbContext db_context =
NSG_Helpers.GetSqliteMemoryDbContext(NSG_Helpers.GetSqliteMemoryConnection(), services);
UserManager<ApplicationUser> userManager =
services.BuildServiceProvider().GetService<UserManager<ApplicationUser>>();
RoleManager<ApplicationRole> roleManager =
services.BuildServiceProvider().GetService<RoleManager<ApplicationRole>>();
DatabaseSeeder _seeder =
new DatabaseSeeder(db_context, userManager, roleManager);
_seeder.Seed().Wait();
}
//
}
}
This is a proof of concept test of accessing the home page:
namespace NSG.NetIncident4.Core_Tests.Infrastructure
{
[TestFixture]
public class EmailConfirmation_UnitTests : UnitTestFixture
{
//
public IConfiguration Configuration { get; set; }
Mock<IEmailSender> _emailSender = null;
[SetUp]
public void MySetup()
{
Fixture_ControllerTestSetup();
_emailSender = new Mock<IEmailSender>();
}
[Test()]
public async Task Home_Page_Test()
{
var response = await client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.AreEqual("Net-Incident Web API Services", responseString);
}
//
}
}
I do not know if HttpClient is properly configured, so this is the values of client:
Related
In ASP.NET Core-6 Web API Application, I am implementing MailKit. So I have this code:
I have this in the Utilities.
EmailConfigurations:
public class EmailConfigurations
{
public string SmtpHost { get; set; }
public string SenderName { get; set; }
public string SenderEmail { get; set; }
public int SmtpPort { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
Then the Service.
MailService:
public class MailService : IMailService
{
private readonly ILogger _logger;
private readonly EmailConfigurations _emailConfig;
public MailService(
ILogger logger,
EmailConfigurations emailConfig
)
{
_logger = logger;
_emailConfig = emailConfig;
}
public async Task<bool> SendEmailAsync(MailRequest mailRequest)
{
var email = new MimeMessage { Sender = MailboxAddress.Parse(_emailConfig.SenderEmail) };
email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
email.Subject = mailRequest.Subject;
var builder = new BodyBuilder();
if (mailRequest.Attachments != null)
{
foreach (var file in mailRequest.Attachments.Where(file => file.Length > 0))
{
byte[] fileBytes;
await using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
builder.Attachments.Add((file.FileName + Guid.NewGuid().ToString()), fileBytes, ContentType.Parse(file.ContentType));
}
}
builder.HtmlBody = mailRequest.Body;
email.Body = builder.ToMessageBody();
try
{
using var smtp = new SmtpClient();
smtp.Connect(_emailConfig.SmtpHost, _emailConfig.SmtpPort, MailKit.Security.SecureSocketOptions.None);
smtp.AuthenticationMechanisms.Remove("XOAUTH2");
smtp.CheckCertificateRevocation = false;
await smtp.SendAsync(email);
smtp.Disconnect(true);
return true;
}
catch (Exception e)
{
_logger.Error(e, e.Source, e.InnerException, e.Message, e.ToString());
return false;
}
}
}
EmailSender:
public class EmailSender : IEmailSender
{
private readonly EmailConfigurations _emailConfig;
private readonly ILogger _logger;
public EmailSender(
EmailConfigurations emailConfig,
ILogger logger
)
{
_emailConfig = emailConfig;
_logger = logger;
}
public void SendEmail(Message message)
{
var emailMessage = CreateEmailMessage(message);
Send(emailMessage);
}
public async Task SendEmailAsync(Message message)
{
var mailMessage = CreateEmailMessage(message);
await SendAsync(mailMessage);
}
private MimeMessage CreateEmailMessage(Message message)
{
var emailMessage = new MimeMessage();
emailMessage.From.Add(new MailboxAddress(_emailConfig.SenderEmail));
emailMessage.To.AddRange(message.To);
emailMessage.Subject = message.Subject;
var bodyBuilder = new BodyBuilder { HtmlBody = string.Format("<h2 style='color:red;'>{0}</h2>", message.Content) };
if (message.Attachments != null && message.Attachments.Any())
{
byte[] fileBytes;
foreach (var attachment in message.Attachments)
{
using (var ms = new MemoryStream())
{
attachment.CopyTo(ms);
fileBytes = ms.ToArray();
}
bodyBuilder.Attachments.Add(attachment.FileName, fileBytes, ContentType.Parse(attachment.ContentType));
}
}
emailMessage.Body = bodyBuilder.ToMessageBody();
return emailMessage;
}
private void Send(MimeMessage mailMessage)
{
using (var client = new SmtpClient())
{
try
{
client.Connect(_emailConfig.SmtpHost, _emailConfig.SmtpPort, MailKit.Security.SecureSocketOptions.None);
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.CheckCertificateRevocation = false;
client.Send(mailMessage);
}
catch (Exception ex)
{
_logger.Information(JsonConvert.SerializeObject(ex));
throw;
}
finally
{
client.Disconnect(true);
client.Dispose();
}
}
}
private async Task SendAsync(MimeMessage mailMessage)
{
using (var client = new SmtpClient())
{
try
{
// client.Connect(_emailConfig.SmtpHost, _emailConfig.SmtpPort, true);
client.Connect(_emailConfig.SmtpHost, _emailConfig.SmtpPort, MailKit.Security.SecureSocketOptions.None);
client.AuthenticationMechanisms.Remove("XOAUTH2");
// client.Authenticate(_emailConfig.Username, _emailConfig.Password);
client.CheckCertificateRevocation = false;
await client.SendAsync(mailMessage);
}
catch (Exception ex)
{
_logger.Information(JsonConvert.SerializeObject(ex));
throw;
}
finally
{
await client.DisconnectAsync(true);
client.Dispose();
}
}
}
}
Then I have the Dependency Injection.
public static class DIServiceExtension
{
public static void AddDependencyInjection(this IServiceCollection services)
{
// Add Service Injections Here
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<ITokenGeneratorService, TokenGeneratorService>();
services.AddScoped<ITokenRepository, TokenRepository>();
services.AddTransient<IMailService, MailService>();
services.AddScoped<IEmailSender, EmailSender>();
}
}
Program.cs:
// Register Dependency Injection Service Extension
builder.Services.AddDependencyInjection();
var app = builder.Build();
But I got this error:
System.AggregateException
HResult=0x80131500
Message=Some services are not able to be constructed (Error while validating the service descriptor
'ServiceType: Lms.Application.Services.Abstract.IEmailSender Lifetime: Scoped ImplementationType:
Lms.Application.Services.Concrete.EmailSender': Unable to resolve service for type
'Lms.Application.Utilities.v1.Common.EmailConfigurations' while attempting to activate 'Lms.Application.Services.Concrete.EmailSender'.)
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()
at Program.<<Main>$>d__0.MoveNext() in C:\Lms.WebApi\Program.cs:line 172
at Program.<Main>(String[] args)
This exception was originally thrown at this call stack:
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(System.Type, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain, System.Reflection.ParameterInfo[], bool)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Microsoft.Extensions.DependencyInjection.ServiceLookup.ResultCache, System.Type, System.Type, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Microsoft.Extensions.DependencyInjection.ServiceDescriptor, System.Type, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain, int)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(Microsoft.Extensions.DependencyInjection.ServiceDescriptor, Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain)
Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(Microsoft.Extensions.DependencyInjection.ServiceDescriptor)
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: Lms.Application.Services.Abstract.IMailService Lifetime: Transient ImplementationType: Lms.Application.Services.Concrete.MailService': Unable to resolve service for type 'Lms.Application.Utilities.v1.Common.EmailConfigurations' while attempting to activate 'Lms.Application.Services.Concrete.MailService'.
Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'Lms.Application.Utilities.v1.Common.EmailConfigurations' while attempting to activate 'Lms.Application.Services.Concrete.MailService'.
C:\Lms.WebApi\Program.cs:line 172
is
var app = builder.Build();
Where did I get it wrong and how do I resolve this?
As mentioned in the comment, you didn't register the EmailConfigurations as a dependency.
Assume that you are extracting the value from appsettings.json as below:
{
"EmailConfigurations": {
"SmtpHost": "Value of SmtpHost",
"SenderName": "Value of SenderName",
// Other properties
}
}
Bind the configuration value to EmailConfigurations class.
builder.Services.Configure<EmailConfigurations>(
builder.Configuration.GetSection("EmailConfigurations"));
In MailService, get the injected dependency for EmailConfigurations with IOptions<EmailConfigurations> and its value.
public class MailService : IMailService
{
private readonly ILogger _logger;
private readonly EmailConfigurations _emailConfig;
public MailService(
ILogger logger,
IOption<EmailConfigurations> emailConfigOption
)
{
_logger = logger;
_emailConfig = emailConfigOption.Value;
}
...
}
Sames goes to EmailSender, follow step 2.
public class EmailSender : IEmailSender
{
private readonly EmailConfigurations _emailConfig;
private readonly ILogger _logger;
public EmailSender(
IOption<EmailConfigurations> emailConfigOption,
ILogger logger
)
{
_emailConfig = emailConfigOption.Value;
_logger = logger;
}
...
}
Reference: Bind hierarchical configuration data using the options pattern
I was using my unit tests on .Net5(or lower) with DependencyResolverHelper like this below. This is my base test class
public abstract class BaseTestClass
{
protected DependencyResolverHelper _serviceProvider;
public BaseTestClass()
{
var webHost = WebHost.CreateDefaultBuilder()
.UseStartup<Startup>()
.Build();
_serviceProvider = new DependencyResolverHelper(webHost);
}
}
and my DependencyResolverHelper
public class DependencyResolverHelper
{
private readonly IWebHost _webHost;
/// <inheritdoc />
public DependencyResolverHelper(IWebHost webHost) => _webHost = webHost;
public T GetService<T>()
{
var serviceScope = _webHost.Services.CreateScope();
var services = serviceScope.ServiceProvider;
try
{
var scopedService = services.GetRequiredService<T>();
return scopedService;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
but im bit confused with new .NET 6 Dependency things. Does anyone tried it without startup class?
I tried to change it with
WebApplicationBuilder
but it gave the
No service for type 'MinimalAPI.Services.TokenService.ITokenService' has been registered. error.
Just Because this part of codes .UseStartup<Startup>() called startup class and registed the services for you,If you try with WebApplicationBuilder in .net 6,You have to regist the services yourself,
I tried as below:
public void Test1()
{
var builder = WebApplication.CreateBuilder(new WebApplicationOptions() { });
builder.Services.AddSingleton<ISomeService,SomeService>();
var app = builder.Build();
var serviceProvider = new DependencyResolverHelper(app);
var someservice = serviceProvider.GetService<ISomeService>();
Assert.Equal(typeof(SomeService), someservice.GetType());
}
DependencyResolverHelper class:
public class DependencyResolverHelper
{
private readonly WebApplication _app;
/// <inheritdoc />
public DependencyResolverHelper(WebApplication app) => _app = app;
public T GetService<T>()
{
var serviceScope = _app.Services.CreateScope();
var services = serviceScope.ServiceProvider;
try
{
var scopedService = services.GetRequiredService<T>();
return scopedService;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
Result:
Ok, I am making a Api, trying to use DI.
My Controller:
[Route("api/[controller]")]
[ApiController]
public class TerminalsController : ControllerBase
{
private readonly IServiceWrapper _service;
public TerminalsController(IServiceWrapper service)
{
_service = service;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] Message object)
{
try
{
Result result = await _service.Terminal.UpsertInfo(ternminalObject);
if (result.shopId != -1 || result.deviceId != -1 || result.companyId != -1)
{
return Ok(result);
}
else
{
return BadRequest("Can not save info from session on database");
}
}
catch (Exception ex)
{
return StatusCode(500, "Internal server error");
}
}
}
And the code of my service:
public class TerminalService : ITerminalService
{
private readonly IRepositoryWrapper _repository;
public TerminalService(IRepositoryWrapper repository)
{
_repository = repository;
}
public async Task<Result> UpsertInfo(company company)
{
try
{
var company = await _repository.Company.GetById(int.Parse(company.Id))
return result;
}
catch (Exception ex)
{
throw ex;
}
}
}
When my code rise the line
var company = await _repository.Company.GetById(int.Parse(company.Id))
I get the error
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Here there are my others class:
My factory:
public class DbClientFactory<T>
{
private static Lazy<T> _factoryLazy = new Lazy<T>(
() => (T)FormatterServices.GetUninitializedObject(typeof(T)),
LazyThreadSafetyMode.ExecutionAndPublication);
public static T Instance
{
get
{
return _factoryLazy.Value;
}
}
}
The factory instace the service and the repositories.
This is my StartUp.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
SqlHelper.connectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING");
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureCors();
services.AddMvc();
services.ConfigureServiceWrapper();
services.ConfigureRepositoryWrapper();
services.AddControllers().AddNewtonsoftJson();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
The ConfigureRepositoryWrapper and the ConfigureServiceWrapper are in the ServiceExtensions.cs:
public static class ServiceExtensions
{
public static void ConfigureCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod());
});
}
public static void ConfigureRepositoryWrapper(this IServiceCollection services)
{
services.AddScoped<IRepositoryWrapper, RepositoryWrapper>();
}
public static void ConfigureServiceWrapper(this IServiceCollection services)
{
services.AddScoped<IServiceWrapper, ServiceWrapper>();
}
}
The implement of ServiceWrapper is:
public class ServiceWrapper : IServiceWrapper
{
private ITerminalService _terminal;
public ITerminalService Terminal {
get
{
if (_terminal == null)
{
_terminal = DbClientFactory<TerminalService>.Instance;
}
return _terminal;
}
}
}
And the implement of RepositoryWrapper is:
public class RepositoryWrapper : IRepositoryWrapper
{
private IDeviceRepository _device;
private IShopRepository _shop;
private ICompanyRepository _company;
public IDeviceRepository Device
{
get
{
if (_device == null)
{
_device = DbClientFactory<DeviceRepository>.Instance;
}
return _device;
}
}
public IShopRepository Shop
{
get
{
if (_shop == null)
{
_shop = DbClientFactory<ShopRepository>.Instance;
}
return _shop;
}
}
public ICompanyRepository Company {
get {
if (_company == null)
{
_company = DbClientFactory<CompanyRepository>.Instance;
}
return _company;
}
}
}
I really dont know what is wrong here...
Thank you!
You are getting this error because company.Id does not exist.
var company = await _repository.Company.GetById(int.Parse(company.Id));
You are creating/fetching an object named company and at the same already have an object passed in that is named company, you can not do have two variables or objects in the same scope with the same name. Rename one of the objects from company to something else.
Also you likely are not passing in an object in the parameter, you are probably passing in null which is the reason for company.Id not existing. Check the value you are passing in, and rename one of the items.
var newCompany = await _repository.Company.GetById(int.Parse(company.Id));
If the exception is happening on _repository, the reason is that you are trying to pass a dependency into a non service class. To fix it, pass the dependency into the controller, then when initializing TerminalService just pass in the _repository. Also make sure that _service.Terminal is instantiated.
public class TerminalsController : ControllerBase
{
private readonly IServiceWrapper _service;
private readonly IRepositoryWrapper _repository;
public TerminalsController(IServiceWrapper service, IRepositoryWrapper repository)
{
_service = service;
_repository = repository;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] Message object)
{
_service.Terminal = new TerminalService(_repository);
Result result = await _service.Terminal.UpsertInfo(ternminalObject);
//remaining code
}
}
I have the below Generic Class & Interface implementations
public interface IHttpClient<T> where T : class
{
public Task<List<T>> GetJsonAsync(string url);
}
public class HttpClient<T> : IHttpClient<T> where T:class
{
private readonly IHttpClientFactory _clientFactory;
public HttpClient(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<List<T>> GetJsonAsync(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get,url);
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<T>>(result);
}
return null;
}
}
and this is how I try to register them in the startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IHttpClient<>), typeof(HttpClient<>));
}
My Controller:
private readonly IHttpClient<Feed> _feedClient;
public HomeController( IHttpClient<Feed> _client)
{
_feedClient = _client;
}
and this is the error I'm getting
InvalidOperationException: Unable to resolve service for type 'System.Net.Http.IHttpClientFactory' while attempting to activate...
what is it that I'm missing? any help is very appreciated..
You should register HttpClient in startup class like this
//register
services.AddHttpClient();
use
public YourController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
var client = _httpClientFactory.CreateClient();
Another options
//register
services.AddHttpClient("YourClientName", c =>
{
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
c.BaseAddress = new Uri("https://yoururl");
});
use
public YourController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
var client = _httpClientFactory.CreateClient("YourClientName");
I'm having an issue my unitofwork doesn't create instance of AppSettings whenever it is called. Unitofwork is for my repository data layer.
This error comes out:
An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an
object. Core.UnitOfWork..ctor() in UnitOfWork.cs, line 24
Stack Query Cookies Headers NullReferenceException: Object reference
not set to an instance of an object. Core.UnitOfWork..ctor() in
UnitOfWork.cs
+
_connection = new SqlConnection(App.GetConnectionString());
Core.Service.UserService.Login(User entity) in UserService.cs
+
using (var uow = new UnitOfWork(/connStr/)) SRTManagement.Controllers.LoginController+d__6.MoveNext() in
LoginController.cs
+
var _user = service.Login(user);
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IAppSettings,AppSettings>();
services.AddMvc();
}
IAppSettings.cs
namespace Core.Etc
{
public interface IAppSettings
{
string GetConnectionString();
}
}
AppSettings.cs
namespace Core.Etc
{
public class AppSettings : IAppSettings
{
public readonly string _connectionString;
public AppSettings(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("DefaultConnection");
}
public string GetConnectionString()
{
return _connectionString;
}
}
}
UnitOfWork.cs
namespace Core
{
public class UnitOfWork : IUnitOfWork
{
private IDbConnection _connection;
private IDbTransaction _transaction;
private IUserRepository _user;
private IRoleRepository _role;
private IAppSettings App;
private bool _disposed;
private bool _token;
public UnitOfWork()
{
_connection = new SqlConnection(App.GetConnectionString());
_connection.Open();
_transaction = _connection.BeginTransaction();
_token = false;
}
public IUserRepository UserRepository
{
get { return _user ?? (_user = new UserRepository(_transaction)); }
}
public IRoleRepository RoleRepository
{
get { return _role ?? (_role = new RoleRepository(_transaction)); }
}
public bool Success()
{
return _token;
}
public void Commit()
{
try
{
_transaction.Commit();
_token = true;
}
catch
{
_transaction.Rollback();
_token = false;
throw;
}
finally
{
_transaction.Dispose();
_transaction = _connection.BeginTransaction();
ResetRepositories();
}
}
private void ResetRepositories()
{
_user = null;
_role = null;
App = null;
}
public void Dispose()
{
DisposeConn(true);
GC.SuppressFinalize(this);
}
private void DisposeConn(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if(_transaction != null)
{
_transaction.Dispose();
_transaction = null;
}
if(_connection != null)
{
_connection.Dispose();
_connection = null;
}
}
_disposed = true;
}
}
~UnitOfWork()
{
DisposeConn(false);
}
}
}
IAppSettings is not being injected into your UnitOfWork, so it will be null when call as you have it
public class UnitOfWork : IUnitOfWork {
private IDbConnection _connection;
private IDbTransaction _transaction;
private IUserRepository _user;
private IRoleRepository _role;
private IAppSettings App;
private bool _disposed;
private bool _token;
public UnitOfWork(IAppSettings App) {
this.App = App;
_connection = new SqlConnection(App.GetConnectionString());
_connection.Open();
_transaction = _connection.BeginTransaction();
_token = false;
}
//Remove the rest of the code for brevity
}
Assuming UnitOfWork is also registered with the service collection.
public void ConfigureServices(IServiceCollection services) {
services.AddTransient<IAppSettings, AppSettings>();
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddMvc();
}
I would also suggest rethinking the current design and avoid tightly coupling the UoW to implementation concerns like SqlConnection.
If staying with ADO then consider using a IDbConnectionFactory abstraction.
public class MyDbConnectionFactory : IDbConnectionFactory {
private readonly IAppSettings appSettings;
public MyDbConnectionFactory(IAppSettings appSettings) {
this.appSettings = appSettings;
}
public IDbConnection CreateConnection() {
return new SqlConnection(appSettings.GetConnectionString());
}
}
Which would let the UoW to be refactored to
public class UnitOfWork : IUnitOfWork {
private IDbConnection _connection;
private IDbTransaction _transaction;
private IUserRepository _user;
private IRoleRepository _role;
private bool _disposed;
private bool _token;
public UnitOfWork(IDbConnectionFactory factory) {
_connection = factory.CreateConnection();
_connection.Open();
_transaction = _connection.BeginTransaction();
_token = false;
}
//Remove the rest of the code for brevity
}
With usual service registrations
public void ConfigureServices(IServiceCollection services) {
services.AddTransient<IAppSettings, AppSettings>();
services.AddTransient<IDbConnectionFactory, MyDbConnectionFactory>();
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddMvc();
}