XUnit Test different results under netcoreapp1.1 and net462 - c#

I created a XUnit test projects in VS 2017, its target framework is netcoreapp1.1, and below code works correctly.
using Xunit;
using Xunit.Abstractions;
using Xunit.Ioc.Autofac;
namespace XUnitTestProject2
{
[UseAutofacTestFramework]
public class MyAwesomeTests
{
public MyAwesomeTests()
{
}
public MyAwesomeTests(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper;
}
[Fact]
public void AssertThatWeDoStuff()
{
_outputHelper.WriteLine("Hello");
}
private readonly ITestOutputHelper _outputHelper;
}
}
using Autofac;
using System.Reflection;
using Xunit;
using Xunit.Abstractions;
using Xunit.Ioc.Autofac;
using Xunit.Sdk;
[assembly: TestFramework("XUnitTestProject2.ConfigureTestFramework", "XUnitTestProject2")]
namespace XUnitTestProject2
{
public class ConfigureTestFramework : AutofacTestFramework
{
private const string TestSuffixConvention = "Tests";
public ConfigureTestFramework(IMessageSink diagnosticMessageSink)
: base(diagnosticMessageSink)
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(typeof(ConfigureTestFramework).GetTypeInfo().Assembly)
.Where(t => t.Name.EndsWith(TestSuffixConvention));
builder.RegisterType<TestOutputHelper>().AsSelf().As<ITestOutputHelper>().InstancePerLifetimeScope();
// configure your container
// e.g. builder.RegisterModule<TestOverrideModule>();
Container = builder.Build();
}
}
}
But, if I change the targetframework to net462, there will not tests be found.
Did i miss anything?
Any help would be appreciated.

Related

xUnit testing controller with error when instantiating JWT token provider

I have a controller in my WebAPI (not MVC or Core) using .Net 4.8 that I'm trying to test with xUnit.
Before I proceed, the way it is setup may not be correct for unit testing, but it is what I have been given and I don't know any better at this stage, any pointers in this regard is welcome.
The main problem in the controller is the immediate declaration/instantiation of ApiOAuthProvider. As it creates the object, it in-turn does an immediate declaration/instantiation of JwtTokenProvider. This is where the error occurs, inner exception:
Message "Could not load file or assembly
'System.Configuration.ConfigurationManager, Version=0.0.0.0,
Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot
find the file specified."
But I'm jumping ahead I'll show the code
Controller:
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TA.Services.BL.BusinessLogic;
using TA.Services.WebAPI.Providers;
using TA.Services.DTO.DTO;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using TA.Services.BL.Interface;
namespace TA.Services.WebAPI.Controllers
{
public class AccountsController : ApiController
{
private readonly ApiOAuthProvider _jwtTokenProvider = new ApiOAuthProvider();
private readonly ICommonBL _commonBL;
private readonly ISystemSettingsBL _systemSettingsBL;
public AccountsController(ICommonBL commonBL, ISystemSettingsBL systemSettingsBL)
{
_commonBL = commonBL;
_systemSettingsBL = systemSettingsBL;
}
...
// GET: JwtToken
[HttpPost]
//[EnableCors("*", "*", "*")]
public JObject Token(User loginUserCredentials)
{
// first check the user name & password is valid one before generating token for that user.
var user = _commonBL.GetUserInformation(loginUserCredentials.UserName);
var accessToken = _jwtTokenProvider.GenerateToken(user);
return accessToken;
}
}
}
ApiOAuthProvider:
namespace TA.Services.WebAPI.Providers
{
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Web.Http.Cors;
using TA.Services.DTO.DTO;
using static TA.Services.WebAPI.Providers.JWTHelper;
[EnableCors("*", "*", "*")]
public class ApiOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly JwtTokenProvider _jwtTokenProvider = new JwtTokenProvider();
[EnableCors("*", "*", "*")]
public JObject GenerateToken(User userDetail)
{
...
return tokenResponse;
}
public static AuthenticationProperties CreateProperties(string userName, Status apiData)
{
...
return new AuthenticationProperties(data);
}
}
}
JwtTokenProvider:
namespace TA.Services.WebAPI.Providers
{
using System;
using System.Configuration;
using System.Security.Claims;
using System.Text;
using System.Web.Configuration;
using System.Web;
using Microsoft.Owin.Security;
using TA.Services.BL.Interface;
using TA.Services.DTO.DTO;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
public class JwtTokenProvider : ISecureDataFormat<AuthenticationTicket>
{
private static string plainTextSecurityKey = ((MachineKeySection)ConfigurationManager.GetSection("system.web/machineKey")).ValidationKey;
private readonly Microsoft.IdentityModel.Tokens.SymmetricSecurityKey _signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(Encoding.UTF8.GetBytes(plainTextSecurityKey));
private readonly IAuditsBL _auditsBL;
public string Protect(AuthenticationTicket data)
{
var signedAndEncodedToken = string.Empty;
...
return signedAndEncodedToken;
}
public AuthenticationTicket Unprotect(string signedAndEncodedToken)
{
ClaimsIdentity idenity = null;
...
return new AuthenticationTicket(idenity, new AuthenticationProperties());
}
}
}
My xUnit test class:
using Moq;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Principal;
using System.Web.Http;
using TA.Services.BL.Interface;
using TA.Services.DTO.DTO;
using TA.Services.WebAPI.Controllers;
using TA.Services.WebAPI.Providers;
using Xunit;
namespace TA.Services.WebAPI.Tests.Controllers
{
public class AccountsControllerTests : BaseUnitTest
{
private readonly Mock<ICommonBL> _commonBLMock;
private readonly Mock<ISystemSettingsBL> _systemSettingsBLMock;
private readonly AccountsController _accountsController;
public AccountsControllerTests()
{
_commonBLMock = new Mock<ICommonBL>();
_systemSettingsBLMock = new Mock<ISystemSettingsBL>();
_accountsController = new AccountsController(_commonBLMock.Object, _systemSettingsBLMock.Object);
}
...
}
}
The exception occurs here in JwtTokenProvider:
private readonly Microsoft.IdentityModel.Tokens.SymmetricSecurityKey _signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(Encoding.UTF8.GetBytes(plainTextSecurityKey));
StackTrace:
at TA.Services.WebAPI.Providers.JwtTokenProvider..ctor() in C:\TA\Repos\April 20 2021\TADev\Service\TA.Services\TA.Services.WebAPI\Providers\JwtTokenProvider.cs:line 19
at TA.Services.WebAPI.Providers.ApiOAuthProvider..ctor() in C:\TA\Repos\April 20 2021\TADev\Service\TA.Services\TA.Services.WebAPI\Providers\ApiOAuthProvider.cs:line 23
at TA.Services.WebAPI.Controllers.AccountsController..ctor(ICommonBL commonBL, ISystemSettingsBL systemSettingsBL) in C:\TA\Repos\April 20 2021\TADev\Service\TA.Services\TA.Services.WebAPI\Controllers\AccountsController.cs:line 27
at TA.Services.WebAPI.Tests.Controllers.AccountsControllerTests..ctor() in C:\TA\Repos\April 20 2021\TADev\Service\TA.Services\Testing\TA.Services.WebAPI.Tests\Controllers\AccountsControllerTests.cs:line 26
Sub-questions:
Maybe I'm going down the wrong path, however should I mock ApiOAuthProvider, in order to do that I need to inject it, in order to do that, I need to create an interface and registry it as a service, but this doesn't sound right.
Do I just need to create a valid token.
Maybe it really can't find the System.Configuration.ConfigurationManager
Main Question: How should I go about resolving this?
Note: The application works just fine outside of this test, under normal execution.
[Update] Changed controller have ApiOAuthProvider injected
public class AccountsController : ApiController
{
private readonly ApiOAuthProvider _jwtTokenProvider;
private readonly ICommonBL _commonBL;
private readonly ISystemSettingsBL _systemSettingsBL;
public AccountsController(ApiOAuthProvider jwtTokenProvider, ICommonBL commonBL, ISystemSettingsBL systemSettingsBL)
{
_commonBL = commonBL;
_systemSettingsBL = systemSettingsBL;
_jwtTokenProvider = jwtTokenProvider;
}
Added ApiOAuthProvider to services in startup:
services.AddSingleton<ApiOAuthProvider>();
Changed test script:
public class AccountsControllerTests : BaseUnitTest
{
private readonly Mock<ApiOAuthProvider> _apiOAuthProviderMock;
private readonly Mock<ICommonBL> _commonBLMock;
private readonly Mock<ISystemSettingsBL> _systemSettingsBLMock;
private readonly AccountsController _accountsController;
public AccountsControllerTests()
{
_apiOAuthProviderMock = new Mock<ApiOAuthProvider>();
_commonBLMock = new Mock<ICommonBL>();
_systemSettingsBLMock = new Mock<ISystemSettingsBL>();
_accountsController = new AccountsController(_apiOAuthProviderMock.Object, _commonBLMock.Object, _systemSettingsBLMock.Object);
}
...
Debugged the test and it kept going until it got to the same error. Doesn't seem to mock very well without an interface.
[Update End]

Overriding dispose method in multiple child classes

I am currently using Xunit to write some integration testing in my .NET Core project. As part of this, I am setting up the data and other information before running a test in my custom class.
using Microsoft.AspNetCore.TestHost;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace XUnitTestProject1
{
public class BaseClassFixture<TStartup> : IAsyncLifetime where TStartup : class
{
private readonly TestServer _server;
public HttpClient Client { get; }
public BaseClassFixture()
{
Client = new HttpClient();
Client.BaseAddress = new Uri("https://apple.com");
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_server?.Dispose();
this.Client?.Dispose();
}
}
public Task DisposeAsync()
{
return Task.CompletedTask;
}
public Task InitializeAsync()
{
throw new NotImplementedException();
}
}
}
My first Child Class:
using Microsoft.AspNetCore.TestHost;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace XUnitTestProject1
{
public class GoogleTestClassFixture<TStartUp> : BaseClassFixture<TStartUp>, IAsyncLifetime where TStartUp : class
{
private readonly TestServer _server;
public HttpClient GoogleClient { get; }
public GoogleTestClassFixture()
{
GoogleClient = new HttpClient();
GoogleClient.BaseAddress = new Uri("https://google.com");
GoogleClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void Dispose()
{
_server?.Dispose();
GoogleClient?.Dispose();
base.Dispose(true);
}
public Task DisposeAsync()
{
throw new NotImplementedException();
}
public Task InitializeAsync()
{
throw new NotImplementedException();
}
}
}
My Second Child Class:
using Microsoft.AspNetCore.TestHost;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace XUnitTestProject1
{
public class FaceBookTestClassFixture<TStartUp> : BaseClassFixture<TStartUp>, IAsyncLifetime where TStartUp : class
{
private readonly TestServer _server;
public HttpClient FaceBookClient { get; }
public FaceBookTestClassFixture()
{
FaceBookClient = new HttpClient();
FaceBookClient.BaseAddress = new Uri("https://facebook.com");
FaceBookClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void Dispose()
{
_server?.Dispose();
FaceBookClient?.Dispose();
base.Dispose(true);
}
public Task DisposeAsync()
{
throw new NotImplementedException();
}
public Task InitializeAsync()
{
throw new NotImplementedException();
}
}
}
When I run all the tests in my test explorer at once, some times I'm getting the error "Cannot access a dispose object". I am assuming, this is happening because some times the tests related to one test fixture are running and the object is disposed, and the other test fixture is unable to access already disposed of items from the base Class fixture.
My First Tests looks something like this:
public class GoogleTests
{
private readonly HttpClient _googleClient;
private readonly HttpClient _baseClient;
public GoogleTests(GoogleTestClassFixture<StartUp> fixture)
{
_googleClient = fixture.GoogleClient;
_baseClient = fixture.Client;
}
[Fact]
public async Task ValidateFirstClient()
{
var getDetails = await _googleClient.GetAsync("//someRoute");
var uri = new Uri("https://someRandom.com");
var postDetails = await _baseClient.PutAsync(uri, getDetails.Content);
var response = await postDetails.Content.ReadAsStringAsync();
dynamic dynamicResponse = JObject.Parse(response);
((int)dynamicResponse.myProperty).Should().Be(0);
}
}
My Second Test Class:
using FluentAssertions;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace XUnitTestProject1
{
public class FaceBookTests
{
private readonly HttpClient _fbClient;
private readonly HttpClient _baseClient;
public FaceBookTests(FaceBookTestClassFixture<StartUp> fixture)
{
_fbClient = fixture.FaceBookClient;
_baseClient = fixture.Client;
}
[Fact]
public async Task ValidateFirstClient()
{
var getDetails = await _fbClient.GetAsync("//someRoute");
var uri = new Uri("https://someRandom.com");
var postDetails = await _baseClient.PutAsync(uri, getDetails.Content);
var response = await postDetails.Content.ReadAsStringAsync();
dynamic dynamicResponse = JObject.Parse(response);
((int)dynamicResponse.myProperty).Should().Be(0);
}
}
}
enter code here
How can I solve this issue, and make sure the object is not disposed until it finishes running all the tests?
Note: I have created a simple web application project just to get the startup.cs class as a reference in my test project

How to do dependency injection on a noncontroller in asp.net core?

I'm trying to create a context for my redis database without using Entity Framework. My connection depends on a ConnectionMultiplexer class, so for unit testing I'm trying to use dependency injection. I'm not sure how to create the using statement for my context without the ConnectionMultiplexer parameter.
Am I doing dependency injection wrong? How can I restructure so I can use the "using" context correctly and have it resolve the dependency for me?
GameSession.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;
namespace Bored.GameService.GameSession
{
public class GameSessionContext : IGameSession, IDisposable
{
private IDatabase conn;
private readonly IConnectionMultiplexer _muxer;
public GameSessionContext(IConnectionMultiplexer muxer)
{
_muxer = muxer;
string connectionString = Startup.Configuration.GetConnectionString("redis");
_muxer = ConnectionMultiplexer.Connect(connectionString);
//conn = muxer.GetDatabase();
//conn.StringSet("foo", "bar");
//var value = conn.StringGet("foo");
//Console.WriteLine(value);
}
public void GetGameState()
{
throw new NotImplementedException();
}
public void AddGameState()
{
throw new NotImplementedException();
}
public void Dispose()
{
_muxer.Dispose();
}
}
}
Here is where I'm registering the dependency in my startup.cs
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionMultiplexer, ConnectionMultiplexer>();
services.AddSignalR();
services.AddCors(options =>
{
options.AddPolicy("ClientPermission", policy =>
{
policy.AllowAnyHeader()
.AllowAnyMethod()
.WithOrigins("http://localhost:3000")
.AllowCredentials();
});
});
}
Here is how I'm using it in my GameServiceAPI class:
using Bored.GameService.Clients;
using Bored.GameService.GameSession;
using Bored.GameService.Models;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace Bored.GameService.GameServiceAPI
{
public class GameServiceHub : Hub<IGameClient>
{
public Task SendMessage(GameMessage message)
{
// Throws an error because GameSessionContext has no IConnectionMultiplexer parameter passed in.
using (var context = new GameSessionContext())
{
// var gameState = context.GetGameState(context);
// context.AddGameState(gameState);
// Clients.All.ReceiveMessage(gameState);
}
return Clients.All.ReceiveMessage(message);
}
}
}
Thanks to #DavidBrowne's suggestion for registering both the GameServiceHub and GameSessionContext with DI.
Here's the code I used to get this to work:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(Configuration.GetConnectionString("redis")));
services.AddScoped<IGameSessionContext, GameSessionContext>();
services.AddSignalR();
services.AddCors(options =>
{
options.AddPolicy("ClientPermission", policy =>
{
policy.AllowAnyHeader()
.AllowAnyMethod()
.WithOrigins("http://localhost:3000")
.AllowCredentials();
});
});
GameSessionContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;
namespace Bored.GameService.GameSession
{
public class GameSessionContext : IGameSessionContext
{
private IDatabase conn;
private readonly IConnectionMultiplexer _muxer;
public GameSessionContext(IConnectionMultiplexer muxer)
{
_muxer = muxer;
}
public string GetGameState()
{
conn = _muxer.GetDatabase();
conn.StringSet("foo", "Here's game state");
return conn.StringGet("foo");
}
public void AddGameState()
{
throw new NotImplementedException();
}
}
}
GameServiceHub.cs
using Bored.GameService.Clients;
using Bored.GameService.GameSession;
using Bored.GameService.Models;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
namespace Bored.GameService.GameServiceAPI
{
public class GameServiceHub : Hub<IGameClient>
{
private IGameSessionContext _context;
public GameServiceHub(IGameSessionContext context)
{
_context = context;
}
public Task SendMessage(GameMessage message)
{
var state = _context.GetGameState();
Console.WriteLine(state);
// var gameState = context.GetGameState(context);
// context.AddGameState(gameState);
return Clients.All.ReceiveMessage(message);
}
}
}

InvalidOperationException: Unable to resolve service for type while attempting to activate

I am creating ASP.NET CORE project using DI and Repository Pattern. When I run project I get this type of error
InvalidOperationException: Unable to resolve service for type
'AD.BLL.Servisi.IKorisnikServis' while attempting to activate
'AD.Web.Controllers.KorisnikController'.
Here is my interface class
using System;
using System.Collections.Generic;
using System.Text;
namespace AD.BLL.Interfejsi
{
public interface IKorisnik
{
public string VratiKorisnike();
}
}
And here is my Service class which call this interface
using AD.BLL.Interfejsi;
using AD.Web.Data;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
namespace AD.BLL.Servisi
{
public class IKorisnikServis : IKorisnik
{
private ApplicationDbContext _db;
public IKorisnikServis(ApplicationDbContext db)
{
_db = db;
}
public string VratiKorisnike()
{
System.DirectoryServices.DirectoryEntry rootDSE = new System.DirectoryServices.DirectoryEntry("LDAP://RootDSE");
var defaultNamingContext = rootDSE.Properties["defaultNamingContext"].Value;
DirectorySearcher dssearch = new DirectorySearcher("LDAP://" + defaultNamingContext);
dssearch.Filter = "(sAMAccountName=ABCDEFGHI)";
SearchResult sresult = dssearch.FindOne();
System.DirectoryServices.DirectoryEntry dsresult = sresult.GetDirectoryEntry();
var Ime = dsresult.Properties["Ime"][0].ToString();
var Prezime = dsresult.Properties["Prezime"][0].ToString();
var LoginName = dsresult.Properties["LoginName"][0].ToString();
var Status = dsresult.Properties["Status"][0].ToString();
var AccountExpired = dsresult.Properties["AccountExpired"][0].ToString();
var PassNevExp = dsresult.Properties["PassNevExp"][0].ToString();
var DomenskaGrupa = dsresult.Properties["DomenskaGrupa"][0].ToString();
var Email = dsresult.Properties["Email"][0].ToString();
return Ime;
}
}
}
Here is my ApplicationDbContext class
using AD.Models.DbModels;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AD.Web.Data
{
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Korisnik> Korisnici { get; set; }
}
}
And here is my Controller action
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AD.BLL.Servisi;
using Microsoft.AspNetCore.Mvc;
namespace AD.Web.Controllers
{
public class KorisnikController : Controller
{
private IKorisnikServis _korisnikServis;
public KorisnikController(IKorisnikServis korisnikServis)
{
_korisnikServis = korisnikServis;
}
public IActionResult VratiKorisnike()
{
_korisnikServis.VratiKorisnike();
return View();
}
public IActionResult Index()
{
return View();
}
}
}
And in Startup.cs I register IKorisnik and IKorisnikServic
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddTransient<IKorisnik, IKorisnikServis>();
}
I checked everything but I cannot see where I made mistake. Any help ?
In your Controller you need to create the filed of type IKorisnik which is your interface instead of IKorisnikServis, So your constructor should be like this:
private IKorisnik _korisnikServis;
public KorisnikController(IKorisnik korisnikServis)
{
_korisnikServis = korisnikServis;
}
However, I would strongly recommend to consider another name for your IKorisnikServis class (KorisnikServis for example) as the prefix I is mostly using to indicate an interface, in this case it is also misleading and I believe that was the reason you have used it in your controller by mistake.

Using MySqlDatabase (MySql.Data.MySqlClient) on Azure Functions v2 - "Unable to resolve service"

I have created an Azure Functions app ~v2 project that uses the MySql.Data.MySqlClient dependency.
The project is setup to also use the SwashBuckle library to create a callable API.
When I execute my project from my local settings, it works fine. Yet, when I publish the Functions app to our Azure server and try testing the function there, I get this error:
System.InvalidOperationException : Unable to resolve service for type
'MyFunctionApp.MySqlDatabase' while attempting to activate
'MyFunctionApp.PostsService'.
at
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp,Type type,Type requiredBy,Boolean isDefaultParameterRequired)
...
My StartUp.cs:
using System;
using System.Reflection;
using AzureFunctions.Extensions.Swashbuckle;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using MyFunctionApp;
[assembly: WebJobsStartup(typeof(SwashBuckleStartup))]
namespace MyFunctionApp
{
internal class SwashBuckleStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
ConfigureServices(builder.Services);
builder.AddSwashBuckle(Assembly.GetExecutingAssembly());
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<MySqlDatabase>(_ => new MySqlDatabase(Environment.GetEnvironmentVariable("MyFunctionApp-DbConn")));
}
}
}
My MySqlDatabase.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
namespace MyFunctionApp
{
public class MySqlDatabase : IDisposable
{
public MySqlConnection Connection;
public MySqlDatabase(string connectionString)
{
Connection = new MySqlConnection(connectionString);
this.Connection.Open();
}
public void Dispose()
{
Connection.Close();
}
}
}
Here's the service I'm calling that's throwing the error mentioned above (PostsService.cs):
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Net;
using MySql.Data.MySqlClient;
namespace MyFunctionApp
{
public class PostsService
{
private readonly MySqlDatabase _MySqlDatabase;
public PostsService(MySqlDatabase mySqlDatabase)
{
_MySqlDatabase = mySqlDatabase;
}
[FunctionName("InsertIntoPost")]
public async Task<IActionResult> InsertIntoPost(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] PostClassObject request,
ILogger log)
{
var cmd = _MySqlDatabase.Connection.CreateCommand() as MySqlCommand;
cmd.CommandText = #"INSERT INTO PostsTable(ID) VALUES (12345)";
int rowCount = await cmd.ExecuteNonQueryAsync();
Console.WriteLine(String.Format("Number of rows inserted={0}", rowCount));
return (ActionResult)new OkObjectResult(1);
}
}
}
I ended up separating my StartUp.cs into two files. Now everything works!
WebJobsStartup.cs:
using AzureFunctions.Extensions.Swashbuckle;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using System.Reflection;
using MyFunctionApp;
[assembly: WebJobsStartup(typeof(WebJobsStartup))]
namespace MyFunctionApp
{
public class WebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddSwashBuckle(Assembly.GetExecutingAssembly());
}
}
}
MyFunctionAppStartUp.cs:
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System;
[assembly: FunctionsStartup(typeof(MyFunctionApp.MyFunctionAppStartup))]
namespace MyFunctionApp
{
public class MyFunctionApp : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<MySqlDatabase>((s) =>
{
return new MySqlDatabase(Environment.GetEnvironmentVariable("MyFunctionApp-DbConn"));
});
builder.Services.AddSingleton<ServiceQueries>();
}
}
}

Categories

Resources