I have a azure functions .net core 2.2 project.
There is a class:
public static class A
{
[FunctionName("abc")]
public static async Task RunAsync(
[ServiceBusTrigger("topic1", "sub1", Connection = "ServiceBusConnectionString")] string msg,
[Inject] IInsertOrderAzureSqlFunction functionRunner)
{
//...
}
}
which uses ServiceBusTrigger. Connection for ServiceBusTrigger is obtained from local.settings.json file. Is it possible to put connection string in different file i.e. secret.settings.json? How to enforce ServiceBusTrigger to get Connection parameter value from other file than local.settings.json
local.settings.json:
{
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true",
"ServiceBusConnectionString": "connectionStringValue1",
"SqlConnection": "connectionStringValue2"
}
}
You can make use of the ConfigurationBuilder to add multiple secrets.settings.json or prod.settings.json etc and load it dynamically. Example code below.
Let's say you have a secrets.settings.json like this
{
"ConnectionStrings": {
"SqlConnectionString": "server=myddatabaseserver;user=tom;password=123;"
},
"MyCustomStringSetting": "Override Some Name",
"MailSettings": {
"PrivateKey": "xYasdf5678asjifSDFGhasn1234sDGFHg"
}
}
Update 1
Make use of Dependency Injection using IWebJobsStartup and you can do it this way.
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyFunctionApp
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddJsonFile("secret.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var myString = config["MyCustomStringSetting"];
builder.Services.PostConfigure<ServiceBusAttribute(serviceBusOptions =>
{
serviceBusOptions.Connection = myString;
});
}
}
}
The trigger will only fall back to using the options.ConnectionStringvalue if the connection string is not set by the attribute. So in your function definition, make sure to set ConnectionStringSetting to "":
Related
I've found that in the application I've been set to work on, there's a TelemetryInitialier which is called at every web api call and adds some properties from the appsettings.json.
Here's the defined class
public class AppInsightTelemetryInitializer : ITelemetryInitializer
{
private readonly AppInsightsMetrics _appInsightsMetrics;
public AppInsightTelemetryInitializer(IOptions<AppInsightsMetrics> appInsightsMetrics)
{
_appInsightsMetrics = appInsightsMetrics.Value;
}
public void Initialize(ITelemetry telemetry)
{
var propTelemetry = (ISupportProperties)telemetry;
propTelemetry.Properties["Application"] = _appInsightsMetrics.Application;
propTelemetry.Properties["Version"] = _appInsightsMetrics.Version;
propTelemetry.Properties["LatestCommit"] = _appInsightsMetrics.LatestCommit;
}
}
This class is registered this way
appBuilder.Services.Configure<AppInsightsMetrics>(appBuilder.Configuration.GetSection(AppInsightsMetrics.InsightsMetrics));
appBuilder.Services.AddSingleton<ITelemetryInitializer, AppInsightTelemetryInitializer>();
if (env.IsDevelopment())
{
services.AddApplicationInsightsTelemetry(x =>
{
x.EnableDebugLogger = false;
x.InstrumentationKey = "instrumentation key";
});
}
else
{
services.AddApplicationInsightsTelemetry();
}
And the data are loaded from the appsettings.json file as
"AppInsightsMetrics": {
"Application": "xxx.Api",
"Version": "",
"LatestCommit": ""
},
Those data are replaced in production by CI/CD azure pipeline.
I was wondering, is there a way of defining them at configuration time and remove this middleware from each call?
Thanks in advance
I have this Configure method in my Startup.cs.
It does 3 things :
serve static files under wwwroot
add CSP header for index.html
serve parameters via /settings.json route
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
// Defaults to index.html
var defaultFilesOptions = new DefaultFilesOptions();
defaultFilesOptions.DefaultFileNames.Clear();
defaultFilesOptions.DefaultFileNames.Add("index.html");
app.UseDefaultFiles(defaultFilesOptions);
var staticFileOptions = new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// Add CSP for index.html
if (ctx.File.Name == "index.html")
{
ctx.Context.Response.Headers.Append(
"Content-Security-Policy", "default-src 'self'" // etc
);
}
}
};
app.UseStaticFiles(staticFileOptions); // wwwroot
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// Settings.json endpoint
endpoints.MapGet("/settings.json", async context =>
{
string json = $#"
{{
""myConfig"": ""{_configuration["myParameter"]}""
}}";
await context.Response.WriteAsync(json);
});
});
}
}
The files under wwwroot are in fact a vue.js app with routing. I need to return index.html for all inexistant request, for the client routing to take control of the page.
Currently it returns a 404 and does not pass in OnPrepareResponse hook.
How can I configure index fallback for the router to work in history mode ?
I think it is acheivable by configuration in web.config, but I'd prefer to configure this in Startup.js so this is all in the same place.
I ended up writing a file provider that does the index fallback. It encapsulate a PhysicalFileProvider, and return index.html under certain conditions if the file is not found. In my case conditions are based on folders css, img or js.
It is implemented this way :
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using System.Linq;
public class IndexFallbackFileProvider : IFileProvider
{
private readonly PhysicalFileProvider _innerProvider;
public IndexFallbackFileProvider(PhysicalFileProvider physicalFileProvider)
{
_innerProvider = physicalFileProvider;
}
public IDirectoryContents GetDirectoryContents(string subpath)
{
return _innerProvider.GetDirectoryContents(subpath);
}
public IFileInfo GetFileInfo(string subpath)
{
var fileInfo = _innerProvider.GetFileInfo(subpath);
if(!fileInfo.Exists && MustFallbackToIndex(subpath))
{
if(!_staticFilesFolders.Any(f => subpath.Contains(f)))
{
fileInfo = _innerProvider.GetFileInfo("/index.html");
}
}
return fileInfo;
}
// Plain 404 are OK for css, img, js.
private static string[] _staticFilesFolders = new string[] { "/css/", "/img/", "/js/" };
private static bool MustFallbackToIndex(string subpath)
{
return !_staticFilesFolders.Any(f => subpath.Contains(f));
}
public IChangeToken Watch(string filter)
{
return _innerProvider.Watch(filter);
}
}
Then, in startup.config, I use this provider.
Plus, I had to set ServeUnknownFileTypes to true to respond to requests to a /path/without/extension.
var physicalFileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "wwwroot"));
var fileProvider = new IndexFallbackFileProvider(physicalFileProvider);
var staticFileOptions = new StaticFileOptions
{
FileProvider = fileProvider,
ServeUnknownFileTypes = true
};
app.UseStaticFiles(staticFileOptions);
I'm writing a .net core 2.2 MVC app, using NLog.
from the Main function, the app writes to all levels of NLog, but from the controllers, nothing is being written.
Logging section in appsettings.json:
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Information"
}
}
Nlog Config:
public static class NlogConfig
{
public static LoggingConfiguration GetLoggingConfiguration()
{
var logConfig = new LoggingConfiguration();
var consoleTarget = new ColoredConsoleTarget("consoleTarget")
{
Layout = #"${date:format=HH\:mm\:ss} ${level} ${message} ${exception}"
};
logConfig.AddTarget(consoleTarget);
logConfig.AddRuleForAllLevels(consoleTarget);
var fatalTarget = new FileTarget("fatal")
{
FileName = "${basedir}/logs/${shortdate}/fatal-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(fatalTarget);
logConfig.AddRuleForOneLevel(LogLevel.Fatal, fatalTarget);
var errorTarget = new FileTarget("error")
{
FileName = "${basedir}/logs/${shortdate}/error-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(errorTarget);
logConfig.AddRuleForOneLevel(LogLevel.Error, errorTarget);
var warningTarget = new FileTarget("warning")
{
FileName = "${basedir}/logs/${shortdate}/warning-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(warningTarget);
logConfig.AddRuleForOneLevel(LogLevel.Warn, warningTarget);
var infoTarget = new FileTarget("info")
{
FileName = "${basedir}/logs/${shortdate}/info-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(infoTarget);
logConfig.AddRuleForOneLevel(LogLevel.Info, infoTarget);
var debugTarget = new FileTarget("debug")
{
FileName = "${basedir}/logs/${shortdate}/debug-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(debugTarget);
logConfig.AddRuleForOneLevel(LogLevel.Debug, debugTarget);
var traceTarget = new FileTarget("trace")
{
FileName = "${basedir}/logs/${shortdate}/trace-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(traceTarget);
logConfig.AddRuleForOneLevel(LogLevel.Trace, traceTarget);
var allTarget = new FileTarget("all")
{
FileName = "${basedir}/logs/${shortdate}/all-${shortdate}.log",
Layout = "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
};
logConfig.AddTarget(allTarget);
logConfig.AddRuleForAllLevels(allTarget);
return logConfig;
}
my main function:
public static void Main(string[] args)
{
// NLog: setup the logger first to catch all errors
var logConfig = NlogConfig.GetLoggingConfiguration();
var logger = NLog.Web.NLogBuilder.ConfigureNLog(logConfig).GetCurrentClassLogger();
try
{
logger.Debug("init main");
logger.Trace("startup - trace logging test");
logger.Info("startup - info logging test");
logger.Warn("startup - warn logging test");
logger.Error("startup - error logging test");
logger.Fatal("startup - fatal logging test");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
logger.Fatal(ex, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
}).UseNLog(); // NLog: setup NLog for Dependency injection
}
and a sample controller:
[ApiController]
[Route("/api/app/parts")]
public class AppPartsController : ControllerBase
{
private readonly ILogger<AppPartsController> _logger;
private IPartsRepository _partsRepo;
public AppPartsController(ILogger<AppPartsController> logger, IPartsRepository repo)
{
_logger = logger;
_partsRepo = repo;
}
[HttpGet]
public async Task<ActionResult> Get()
{
_logger.LogInformation("/api/app/parts has been reached");
try
{
var partsDb = await _partsRepo.GetAllParts();
var parts = Mapper.Map<IEnumerable<AppPartDto>>(partsDb);
return Ok(parts);
}
catch (Exception ex)
{
_logger.LogError(ex, "error in AppPartsController, get all parts ");
return StatusCode(500);
}
}
}
no matter what i do, all the log entries in the 'Main' are written, but the controller entries are not.
afaik, NLog is configured as per their instructions, but obviously I'm missing something here...
Thanks,
Nir
well, there has been a confusion of configs.
when I've looked deeper in the debug folder, I suddenly saw that log files are generated in a place where they shouldn't, with a different naming convention that was in my NLog code config.
this led me on a wide file hunt for a rouge config file - which was subsequently found lurking in the project folder, after being removed from the project, but not deleted from the file system.
I then continued to search for the usages for said config file in my code and found them in all of my repositories.
once the repositories had been converted to use the proper code config, all was fixed, and peace & order had been restored.
I've also updated the logging section in the appsettings.json to look like this (but I'm not really sure if it had any effect):
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Trace"
}
}
I am facing a problem that would like help.
I am developing a background process that will be listening to a queue in rabbitmq server.
It is OK if I run it in a .net core console application. However I would like to do it in a more elegant way such as web service (which has given me a lot of trouble where it does not work when installed) or an IIS hosted web application.
I face a problem of Scoped Service when I try to host the service (IHostedService) in .net core web application.
The code below is working fine in a console application. How can make it run as an IHostedService in a .net core web application.
What am I supposed to change.
Your help is appreciated.
CODE:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using PaymentProcessor.Models;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Microsoft.EntityFrameworkCore;
namespace PaymentProcessor
{
public class PaymentProcessingService : HostedService
{
IConfiguration configuration;
private EntitiesContext claimsContext;
private string connectionString;
private string HostName = "";
private string UserName = "";
private string Password = "";
private static int MaxRetries;
private IConnectionFactory factory;
private IConnection connection;
private IModel channel;
public PaymentProcessingService(IConfiguration configuration)
{
this.configuration = configuration;
this.connectionString = configuration.GetConnectionString ("StagingContext");
claimsContext = new EntitiesContext(connectionString);
HostName = this.configuration.GetValue<string>("Settings:HostName");
UserName = this.configuration.GetValue<string>("Settings:UserName");
Password = this.configuration.GetValue<string>("Settings:Password");
MaxRetries = this.configuration.GetValue<string>("Settings:MaxRetries").ConvertTo<int>();
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
connect:
factory = new ConnectionFactory { HostName = HostName, UserName = UserName, Password = Password };
try
{
connection = factory.CreateConnection();
channel = connection.CreateModel();
channel.ExchangeDeclare("payment_rocessing_exchange", "topic");
channel.QueueDeclare("payment_processing_queue", true, false, false, null);
channel.QueueBind("payment_processing_queue", "payment_processing_exchange", "processing");
var queueArgs = new Dictionary<string, object>
{
{ "x-dead-letter-exchange", "payment_processing_exchange" },
{"x-dead-letter-routing-key", "processing_retry"},
{ "x-message-ttl", 10000 }
};
channel.ExchangeDeclare("payment_rocessing_exchange", "topic");
channel.QueueDeclare("payment_processing_retry_queue", true, false, false, queueArgs);
channel.QueueBind("payment_processing_retry_queue", "payment_processing_exchange", "processing_retry", null);
channel.ExchangeDeclare("payment_processing_exchange", "topic");
channel.QueueDeclare("payment_processing_error_queue", true, false, false, null);
channel.QueueBind("payment_processing_error_queue", "payment_processing_exchange", "processing_error", null);
channel.ExchangeDeclare("payment_processing_exchange", "topic");
channel.QueueDeclare("payment_integration_queue", true, false, false, null);
channel.QueueBind("payment_integration_queue", "payment_processing_exchange", "integration", null);
channel.BasicQos(0, 1, false);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var message = ea.Body.DeSerializeText();
try
{
var saveBundle = JObject.Parse(message);
var msg = (dynamic)((dynamic)saveBundle).Message;
string referenceNo = (string)msg.ReferenceNo;
var parameters = new[]
{
new SqlParameter
{
DbType = DbType.String,
ParameterName = "ReferenceNo",
Value =referenceNo
}
};
var result = claimsContext.Database.ExecuteSqlCommand("dbo.PaymentReferencesProcessSingle #ReferenceNo", parameters);
IBasicProperties props = channel.CreateBasicProperties();
props.Persistent = true;
props.ContentType = "text/plain";
props.DeliveryMode = 2;
channel.BasicPublish("payment_processing_exchange", "integration", props, (new MessageEnvelope { RetryCounts = 0, Message = JObject.FromObject(new { ReferenceNo = referenceNo }) }).Serialize());
}
catch (Exception ex)
{
MessageEnvelope envelope = JsonConvert.DeserializeObject<MessageEnvelope>(message);
if (envelope.RetryCounts < MaxRetries)
{
int RetryCounts = envelope.RetryCounts + 1;
MessageEnvelope messageEnvelope = new MessageEnvelope { RetryCounts = RetryCounts, Message = envelope.Message };
var data = messageEnvelope.Serialize();
channel.BasicPublish("payment_processing_exchange", "processing_retry", null, data);
}
else
{
var data = envelope.Serialize();
channel.BasicPublish("payment_processing_exchange", "processing_error", null, data);
}
}
finally
{
channel.BasicAck(ea.DeliveryTag, false);
}
};
channel.BasicConsume(queue: "payment_processing_queue", autoAck: false, consumer: consumer);
}
catch (Exception ex)
{
Thread.Sleep(10000);
goto connect;
}
}
}
}
and then
services.AddScoped<IHostedService, PaymentProcessingService>();
As Dekim mentioned a service should be registered.
Please have a look on an example I created on GitHub.
Program.cs looks like this:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;
namespace Core
{
internal class Program
{
static async Task Main(string[] args)
{
await new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<ServiceRabbitMQ>(); // register our service here
})
.RunConsoleAsync();
}
}
}
Because the IHostedService requires the creation of a special scope according to the documentation.
From the Microsoft documentation cited in the above answer :
No scope is created for a hosted service by default.
Consider using : services.AddHostedService<MyHostedService>();
I´ve read that the new EF7 allows in memory storage that makes easier the unit testing. This works great on a DbContext, but the problem that I have so far is that I'm using a IdentityDbContext, which doesn´t come with that option, or I haven't found it so far.
What I'm trying to do is to add unit test to my AccountController which depends on the new UserManager and SignInManager classes. I would like to test against an In-Memory list but I still don´t know how to make those clases use a dbcontext different than the EF-SQL IdentityDbContext. Is there any way around this?
Dec. 27th 2015 - Update for RC1
The project.json
{
"version": "1.0.0-*",
"dependencies": {
"{assembly under test}": "",
"Microsoft.AspNet.Hosting": "1.0.0-*",
"xunit": "2.1.0",
"xunit.runner.dnx": "2.1.0-rc1-*"
},
"commands": {
"test": "xunit.runner.dnx"
},
"frameworks": {
"dnx451": {
"dependencies": {
"Moq": "4.2.1312.1622"
}
}
}
}
A test class sample to test AccountController:
using {Your NS}.Controllers;
using Microsoft.AspNet.Identity;
using Moq;
using Xunit;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.OptionsModel;
using System.Threading;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.Extensions.Logging;
namespace NS.test.Controllers
{
public class TestRole
{
public string Id { get; private set; }
public string Name { get; set; }
}
public interface ITestUserStore<T>:IUserStore<T>, IUserPasswordStore<T> where T :class
{ }
public class AccountControllerTest
{
private static UserManager<TUser> GetUserManager<TUser>(List<IUserValidator<TUser>> userValidators) where TUser : class
{
var store = new Mock<ITestUserStore<TUser>>();
store.Setup(s => s.CreateAsync(It.IsAny<TUser>(), It.IsAny<CancellationToken>())).ReturnsAsync(IdentityResult.Success);
var options = new Mock<IOptions<IdentityOptions>>();
var idOptions = new IdentityOptions();
idOptions.Lockout.AllowedForNewUsers = false;
options.Setup(o => o.Value).Returns(idOptions);
var pwdValidators = new List<PasswordValidator<TUser>>();
pwdValidators.Add(new PasswordValidator<TUser>());
var userManager = new UserManager<TUser>(store.Object, options.Object, new PasswordHasher<TUser>(),
userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
new IdentityErrorDescriber(), null,
new Mock<ILogger<UserManager<TUser>>>().Object,
null);
return userManager;
}
private static Mock<SignInManager<TUser>> MockSigninManager<TUser>(UserManager<TUser> userManager) where TUser : class
{
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var roleManager = new RoleManager<TestRole>(new Mock<IRoleStore<TestRole>>().Object,new RoleValidator<TestRole>[] { new RoleValidator<TestRole>() }, null, null, null, null);
var identityOptions = new IdentityOptions();
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Value).Returns(identityOptions);
var claimsFactory = new UserClaimsPrincipalFactory<TUser, TestRole>(userManager, roleManager, options.Object);
return new Mock<SignInManager<TUser>>(userManager, contextAccessor.Object, claimsFactory, options.Object, null);
}
[Fact]
public async Task RegisterTest()
{
var userValidators = new List<IUserValidator<YourUser>>();
var validator = new Mock<IUserValidator<YourUser>>();
userValidators.Add(validator.Object);
var userManager = GetUserManager(userValidators);
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<ChatLeUser>()))
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
var signinManager = MockSigninManager<YourUser>(userManager);
signinManager.Setup(m => m.SignInAsync(It.IsAny<YourUser>(), It.IsAny<bool>(), null)).Returns(Task.FromResult(0)).Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
using (var controller = new AccountController(userManager, signinManager.Object) { ViewData = viewData })
{
var result = await controller.Register(new RegisterViewModel()
{
ConfirmPassword = "test123",
Password = "Test123-123",
UserName = "test"
});
Assert.IsType<RedirectToActionResult>(result);
}
}
}
}
Ultimately, you'll probably want to mock the UserManager and SignInManager directly. Unfortunately, many of those methods under beta-3 aren't virtual; you'll be a bit stuck there. However, you can see from the source of UserManager and SignInManager that the Identity team appears to be addressing those for the next release.
I personally have several tests not compiling because of the same issue.