What is the recommended way to run asp.net identity functions in transaction? - asp.net-mvc-5

Using asp.net identity RTW version.
I need to perform several actions in a transaction, including both UserMananger function calls and other operations on my DbContext (example: create new user, add it to group and perform some business-logic operations).
How should I do this?
My thoughts follow.
TransactionScope
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
// Do what I need
if (everythingIsOk) scope.Complete();
}
The problem is: UserManager functions are all async, and TransactionScope was not designed to work with async/await. It seems to be solved in .Net Framework 4.5.1. But I use Azure Web Sites to host my project builds, so I cannot target 4.5.1 yet.
Database transaction
public class SomeController : Controller
{
private MyDbContext DbContext { get; set; }
private UserManager<User> UserManager { get; set; }
public AccountController()
{
DbContext = new MyDbContext()
var userStore = new UserStore<IdentityUser>(DbContext);
UserManager = new UserManager<IdentityUser>(userStore);
}
public async ActionResult SomeAction()
{
// UserManager uses the same db context, so they can share db transaction
using (var tran = DbContext.Database.BeginTransaction())
{
try
{
// Do what I need
if (everythingIsOk)
tran.Commit();
else
{
tran.Rollback();
}
}
catch (Exception)
{
tran.Rollback();
}
}
}
}
That seems to work, but how can I unit-test it?
UserManager<> constructor accepts IUserStore<>, so I can easily stub it.
UserStore<> constructor accepts DbContext, no idea how I can stub this.

You can implement your own test user store that can be stubbed out for your unit test.
If you want to use the actual EF UserStore in your tests, that also will work, but it will be creating a database using the DefaultConnection string by default. You could specify a DatabaseInitializer to always drop/recreate your tables in your tests if you wanted to ensure a clean db for every test.

Related

Entity Framework Core: different threads using the same instance of DbContext

The application was developed on ASP NET Core 3. To log user actions, I decided to use a single method in the Project class. Faced the problem of using one singleton dbContext from different threads.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
Project.cs
public async Task AddUserLog(string action, string message, int userId)
{
try
{
UserLog userLog = new UserLog()
{
Action = action,
Message = message,
UserId = userId
Datepoint = DateTime.Now
};
_dbContext.UserLog.Add(userLog);
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
await AddSystemLog("Project", "AddUserLog", ex.Message);
}
}
SchemeController.cs
public class SchemeController : ControllerBase
{
private readonly Project _project;
public SchemeController(Project project)
{
_project = project;
}
[Authorize(Policy = "AdvancedControl")]
[HttpPost("[action]")]
public async Task SomeMethode()
{
for (int i = 0; i < 10; i++)
{
await _project.AddUserLog("Text", "Message", 42);
}
}
}
Already at the second iteration of the loop, I catch an exception in the AddUserLog method:
"A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext."
I suggest several solutions:
Add the log to the buffer table and then save it to the database by timer. But this is not the best way out;
Block the method while it is being saved to the database.
But I don’t like any of the options.
Please tell me the correct approach in solving this issue.
So, you trying to use shared resource (singleton Project class) to perform parallel operations (save UserLogs) while your shared resource implementation is not thread-safe (exceptions raised).
You have at lease three ways to solve this:
Do not use shared resource: register Project per scope instead of singletone;
Do not perform operations in parallel: seems hard to achieve because you making webapp and you can't force user(s) to wait
Refactor your resource to be thread-safe: add locks/mutexes/buffering... inside Project
There is no one "correct" way - all 3 are correct. Choose one you like (or combine several).
Usually using scoped dbcontext is recommended (because connections are pooled), but it's the creator of app who should decide.

Factory pattern in DDD

Which is correct or suggested way to use factories in DDD?
Should factory method receive all necessary parameters from application service, or we are allowed to inject repositories and extract needed data inside factory?
Should it be (example 1):
public class UserTokenFactory : IUserTokenFactory
{
IUserTypeResourceRepository _userTypeResourceRepository;
public UserTokenFactory(IUserTypeResourceRepository userTypeResourceRepository)
{
_userTypeResourceRepository = userTypeResourceRepository;
}
public async Task<UserToken> CreateWithAsync(User user)
{
var userTypeResources = await _userTypeResourceRepository.GetByUserTypeIdAsync(user.UserTypeId);
//Some logic for creating user tokens
throw new NotImplementedException();
}
}
or as (Example 2)
public class UserTokenFactory : IUserTokenFactory
{
IUserTypeResourceRepository _userTypeResourceRepository;
public UserTokenFactory(IUserTypeResourceRepository userTypeResourceRepository)
{
_userTypeResourceRepository = userTypeResourceRepository;
}
public UserToken CreateWith(User user, List<UserTypeResource> userTypeResources)
{
//Some logic for creating user tokens
throw new NotImplementedException();
}
}
You are allowed to inject services into factories. Your factory is basically a domain service which happens to create objects. However, I'd probably rely on the ISP here and define an interface like IResolveUserType rather than depending on the wider IUserTypeResourceRepository interface.

Using Catel with Repository Pattern, EF6 and View Models

I cannot find any documentation on connecting a view model to a repository using Catel.
I have set up the Repository Pattern and my Models with EF6 Code First (all extending from ModelBase) but need to know how to use it with a ViewModel.
Do I need to create a service for the UnitOfWork? And if so, how? How will I use this in a ViewModel?
I am currently using the repository as a model in my viewmodel, but i do not think this is the correct way to do it? See my CompaniesViewModel below:
IUnitOfWork uow;
public CompaniesViewModel()
{
uow = new UnitOfWork<SoftwareSolutionsContext>();
CompanyRepository = uow.GetRepository<ICompanyRepository>();
}
public override string Title { get { return "Companies"; } }
protected override async Task Close()
{
uow.Dispose();
await base.Close();
}
protected override async Task Initialize()
{
Companies = new ObservableCollection<Company>(CompanyRepository.GetAll());
await base.Initialize();
}
public ObservableCollection<Company> Companies
{
get { return GetValue<ObservableCollection<Company>>(CompaniesProperty); }
set { SetValue(CompaniesProperty, value); }
}
public static readonly PropertyData CompaniesProperty = RegisterProperty("Companies", typeof(ObservableCollection<Company>), null);
[Model]
public ICompanyRepository CompanyRepository
{
get { return GetValue<ICompanyRepository>(CompanyRepositoryProperty); }
private set { SetValue(CompanyRepositoryProperty, value); }
}
public static readonly PropertyData CompanyRepositoryProperty = RegisterProperty("CompanyRepository", typeof(ICompanyRepository));
Essentially, I have 2 scenarios for working on the data:
getting all the data to display on a datagrid
selecting a record on the datagrid to open another view for editing a single record
Any guidance would be appreciated.
This is a very difficult subject, because there are basically a few options here:
Create abstractions in services (so the VM's only work with services, the services are your API into the db). The services work with the UoW
There are some people thinking that 1 is overcomplicated. In that case, you can simply use the UoW inside your VM's
Both have their pros and cons, just pick what you believe in most.

Is it possible to inject an instance of object to service at runtime

I have created a plugin which inspects a param in the query string and loads up a user object based on this ID and populates
any request DTO with it. (All my request DTO's inherit from BaseRequest which has a CurrentUser property)
public class CurrentUserPlugin : IPlugin
{
public IAppHost CurrentAppHost { get; set; }
public void Register(IAppHost appHost)
{
CurrentAppHost = appHost;
appHost.RequestFilters.Add(ProcessRequest);
}
public void ProcessRequest(IHttpRequest request, IHttpResponse response, object obj)
{
var requestDto = obj as BaseRequest;
if (requestDto == null) return;
if (request.QueryString["userid"] == null)
{
throw new ArgumentNullException("No userid provided");
}
var dataContext = CurrentAppHost.TryResolve<IDataContext>();
requestDto.CurrentUser = dataContext.FindOne<User>(ObjectId.Parse(requestDto.uid));
if (requestDto.CurrentUser == null)
{
throw new ArgumentNullException(string.Format("User [userid:{0}] not found", requestDto.uid));
}
}
}
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there. Is there a way to make data from plugins globally available to my services? I am also wondering if there is another way of instantiating this object as for my unit tests, the Plugin is not run - as I call my service directly.
So, my question is, instead of using Plugins can I inject a user instance to my services at run time? I am already using IoC to inject different Data base handlers depending on running in test mode or not but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
Below is an example of how I inject my DataContext in appHost.
container.Register(x => new MongoContext(x.Resolve<MongoDatabase>()));
container.RegisterAutoWiredAs<MongoContext, IDataContext>();
Here is an example of my BaseService. Ideally I would like to have a CurrentUser property on my service also.
public class BaseService : Service
{
public BaseService(IDataContext dataContext, User user)
{
DataContext = dataContext;
CurrentUser = user; // How can this be injected at runtime?
}
public IDataContext DataContext { get; private set; }
public User CurrentUser { get; set; }
}
Have you thought about trying to use the IHttpRequest Items Dictionary to store objects. You can access these Items from any filter or service or anywhere you can access IHttpRequest. See the src for IHttpRequest.
Just be mindful of the order that your attributes, services and plugins execute and when you store the item in the Items dictionary.
Adding:
We don't want to use HttpContext inside of the Service because we want use Service in our tests directly.
Advantages for living without it
If you don't need to access the HTTP
Request context there is nothing stopping you from having your same
IService implementation processing requests from a message queue which
we've done for internal projects (which incidentally is the motivation
behind the asynconeway endpoint, to signal requests that are safe for
deferred execution).
http://www.servicestack.net/docs/framework/accessing-ihttprequest
And we don't use http calls to run tests.
So our solution is:
public class UserService
{
private readonly IDataContext _dataContext;
public UserService(IDataContext dataContext)
{
_dataContext = dataContext;
}
public User GetUser()
{
var uid = HttpContext.Current.Request.QueryString["userId"];
return _dataContext.Get<User>(uid);
}
}
and
container.Register(x => new UserService(x.Resolve<IDataContext>()).GetUser()).ReusedWithin(ReuseScope.Request);
This is service signature:
public SomeService(IDataContext dataContext, User user) { }
Any suggestions?
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there
How will your application know about the user if you're not passing the 'userid' in the querystring? Could you store the user data in the Session? Using a Session assumes the client is connected to your app and persists a Session Id (ss-id or ss-pid cookie in ServiceStack) in the client that can be looked up on the Server to get the 'session data'. If you can use the Session you can retrieve the data from your service doing something like
base.Session["UserData"] or base.SessionAs<User>();
Note: you will need to save your User data to the Session
Is there a way to make data from plugins globally available to my services? but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
This sounds like you want a global request filter. You're kind of already doing this but you're wrapping it into a Plugin.

Spec fails when run by mspec.exe, but passes when run by TD.NET

I wrote about this topic in another question.
However, I've since refactored my code to get rid of configuration access, thus allowing the specs to pass. Or so I thought. They run fine from within Visual Studio using TestDriven.Net. However, when I run them during rake using the mspec.exe tool, they still fail with a serialization exception. So I've created a completely self-contained example that does basically nothing except setup fake security credentials on the thread. This test passes just fine in TD.Net, but blows up in mspec.exe. Does anybody have any suggestions?
Update: I've discovered a work-around. After researching the issue, it seems the cause is that the assembly containing my principal object is not in the same folder as the mspec.exe. When mspec creates a new AppDomain to run my specs, that new AppDomain has to load the assembly with the principal object in order to deserialize it. That assembly is not in the same folder as the mspec EXE, so it fails. If I copied my assembly into the same folder as mspec, it works fine.
What I still don't understand is why ReSharper and TD.Net can run the test just fine? Do they not use mspec.exe to actually run the tests?
using System;
using System.Security.Principal;
using System.Threading;
using Machine.Specifications;
namespace MSpecTest
{
[Subject(typeof(MyViewModel))]
public class When_security_credentials_are_faked
{
static MyViewModel SUT;
Establish context = SetupFakeSecurityCredentials;
Because of = () =>
SUT = new MyViewModel();
It should_be_initialized = () =>
SUT.Initialized.ShouldBeTrue();
static void SetupFakeSecurityCredentials()
{
Thread.CurrentPrincipal = CreatePrincipal(CreateIdentity());
}
static MyIdentity CreateIdentity()
{
return new MyIdentity(Environment.UserName, "None", true);
}
static MyPrincipal CreatePrincipal(MyIdentity identity)
{
return new MyPrincipal(identity);
}
}
public class MyViewModel
{
public MyViewModel()
{
Initialized = true;
}
public bool Initialized { get; set; }
}
[Serializable]
public class MyPrincipal : IPrincipal
{
private readonly MyIdentity _identity;
public MyPrincipal(MyIdentity identity)
{
_identity = identity;
}
public bool IsInRole(string role)
{
return true;
}
public IIdentity Identity
{
get { return _identity; }
}
}
[Serializable]
public class MyIdentity : IIdentity
{
private readonly string _name;
private readonly string _authenticationType;
private readonly bool _isAuthenticated;
public MyIdentity(string name, string authenticationType, bool isAuthenticated)
{
_name = name;
_isAuthenticated = isAuthenticated;
_authenticationType = authenticationType;
}
public string Name
{
get { return _name; }
}
public string AuthenticationType
{
get { return _authenticationType; }
}
public bool IsAuthenticated
{
get { return _isAuthenticated; }
}
}
}
Dan,
thank you for providing a reproduction.
First off, the console runner works differently than the TestDriven.NET and ReSharper runners. Basically, the console runner has to perform a lot more setup work in that it creates a new AppDomain (plus configuration) for every assembly that is run. This is required to load the .dll.config file for your spec assembly.
Per spec assembly, two AppDomains are created:
The first AppDomain (Console) is created
implicitly when mspec.exe is
executed,
a second AppDomain is created by mspec.exe for the assembly containing the specs (Spec).
Both AppDomains communicate with each other through .NET Remoting: For example, when a spec is executed in the Spec AppDomain, it notifies the Console AppDomain of that fact. When Console receives the notification it acts accordingly by writing the spec information to the console.
This communiciation between Spec and Console is realized transparently through .NET Remoting. One property of .NET Remoting is that some properties of the calling AppDomain (Spec) are automatically included when sending notifications to the target AppDomain (Console). Thread.CurrentPrincipal is such a property. You can read more about that here: http://sontek.vox.com/library/post/re-iprincipal-iidentity-ihttpmodule-serializable.html
The context you provide will run in the Spec AppDomain. You set Thread.CurrentPrincipal in the Because. After Because ran, a notification will be issued to the Console AppDomain. The notification will include your custom MyPrincipal that the receiving Console AppDomain tries to deserialize. It cannot do that since it doesn't know about your spec assembly (as it is not included in its private bin path).
This is why you had to put your spec assembly in the same folder as mspec.exe.
There are two possible workarounds:
Derive MyPrincipal and MyIdentity from MarshalByRefObject so that they can take part in cross-AppDomain communication through a proxy (instead of being serialized)
Set Thread.CurrentPrincipal transiently in the Because
(Text is required for formatting to work -- please ignore)
Because of = () =>
{
var previousPrincipal = Thread.CurrentPrincipal;
try
{
Thread.CurrentPrincipal = new MyPrincipal(...);
SUT = new MyViewModel();
}
finally
{
Thread.CurrentPrincipal = previousPrincipal;
}
}
ReSharper, for example, handles all the communication work for us. MSpec's ReSharper Runner can hook into the existing infrastructure (that, AFAIK, does not use .NET Remoting).

Resources