I have a custom MembershipProvider class that inherits from MembershipProvider that takes two parameters:
public class CustomMembershipProvider : MembershipProvider
{
private readonly ISecurityRepository _securityRepository;
private readonly IUserRepository _userRepository;
public CustomMembershipProvider(ISecurityRepository securityRepository, IUserRepository userRepository)
{
...
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
...
}
... etc
}
The config file for this looks similar to this:
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear />
<add name="CustomMembershipProvider" type="Library.Membership.CustomMembershipProvider" />
</providers>
</membership>
This works fine mostly throughout my web application for logging in and logging out. I am using Unity for DI and have the necessary classes setup in my Boostrapper.cs class.
However I recently ran into an issue when I wanted to create a custom User class and called the Membership.GetUser method. I get the following exception when I do:
{"No parameterless constructor defined for this object. (C:\\*app path*\\web.config line 43)"}
Line 43 in my config file points to the custom membership provider that I posted above. I think that elsewhere the app is using Unity to resolve those parameters but when using the Membership class it doesn't.
Is there any way I can tell the application how to resolve those dependencies or if not is there a way of adding those dependencies to my membership provider without using the concrete implementation?
EDIT 1:
Here is the custom User class:
public class User : MembershipUser
{
public int UserId { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public DateTime LastLoggedOnDate { get; set; }
...
}
EDIT 2:
In my custom membership provider class this is what the GetUser method looks like:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
return _userRepository.GetUser(username);
}
The problem is that you can't inject into the Membership provider via constructor. Refer to this question
I implemented a Custom Membership Provider using Ninject and I used the ServiceLocator to get the instance of the service.
public class AccountMembershipProvider : MembershipProvider
{
private readonly IUsers _users;
public AccountMembershipProvider()
{
_users = ServiceLocator.Current.GetInstance<IUsers>();
}
public override bool ValidateUser(string username, string password)
{
return _users.IsValidLogin(username, password);
}
...
}
In your case, you need to get the IUserRepository and ISecurityRepository.
When you wire your Interfaces/services
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUsers>().To<UsersService>();
kernel.Bind<IRoles>().To<RolesService>();
kernel.Bind<MembershipProvider>().To<AccountMembershipProvider>().InRequestScope();
kernel.Bind<RoleProvider>().To<AccountRoleProvider>().InRequestScope();
}
You can check a complete example of it working (using Ninject but you can adapt it to Unity) here: https://github.com/lopezbertoni/SampleApp
Hope this helps,
I also use Unity and implemented a custom membership provider, but used a slightly different approach. Check the code sample:
/// <summary>
/// Defines the custom membership provider class.
/// </summary>
public class SsoMembershipProvider : MembershipProvider
{
private IApplicationsRepository _appsRepo;
private IUsersRepository _usersRepo;
private IMembershipsRepository _membershipsRepo;
/// <summary>
/// Initializes a new instance of the <see cref="SsoMembershipProvider"/> class
/// using injectionConstructor attribute in order to get the repositories needed.
/// </summary>
/// <param name="appsRepo">The apps repo.</param>
/// <param name="usersRepo">The users repo.</param>
/// <param name="membershipsRepo">The memberships repo.</param>
[InjectionConstructor]
public SsoMembershipProvider(IApplicationsRepository appsRepo, IUsersRepository usersRepo, IMembershipsRepository membershipsRepo)
{
_appsRepo = appsRepo;
_usersRepo = usersRepo;
_membershipsRepo = membershipsRepo;
}
/// <summary>
/// Initializes a new instance of the <see cref="SsoMembershipProvider"/> class.
/// which calls the internal contructor.
/// </summary>
/// <remarks>This is happening due to the fact that membership provider needs a
/// parametless constructor to be initialized</remarks>
public SsoMembershipProvider()
: this(DependencyResolver.Current.GetService<IApplicationsRepository>(),
DependencyResolver.Current.GetService<IUsersRepository>(),
DependencyResolver.Current.GetService<IMembershipsRepository>())
{ }
}
This worked for me
public ICustomerRepository CustomerRepository {
get { return DependencyResolver.Current.GetService<ICustomerRepository>(); }
}
and then use
public override bool ValidateUser(string username, string password)
{
var abc = CustomerRepository.ValidateCustomer(username, password);
}
Related
I have a class as shown below,
public class AccessToken : IAuthToken
{
/// <summary>
/// Initializes a new instance of the <see cref="AccessToken"/> class.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="validTo">The valid to.</param>
public AccessToken(string token, DateTimeOffset validTo)
{
ValidTo = validTo;
Token = token;
}
///<inheritdoc />
public DateTimeOffset ValidTo { get; private set; }
/// <summary>
/// Gets the RAW Jwt token value.
/// This value is encoded and represents the Jwt token.
/// </summary>
/// <value>
/// The token.
/// </value>
public string Token { get; private set; }
}
The DI code goes like this,
return services
.AddTransient<IAuthToken, AccessToken>()
.AddTransient<IAuthTokenService, AuthTokenService>()
.AddSingleton<IIdentityDiscovery, IdentityDiscovery>()
.AddTransient<IIdentityTokenClient, IdentityTokenClient>()
.AddTransient<IDiscoveryClientRetryPolicy, DiscoveryClientRetryPolicy>()
.AddTransient<ITokenClientRetryPolicy, TokenClientRetryPolicy>()
.AddSingleton<IRetryPolicyOptions>(provider => retryOptions);
The whole thing is packaged as a nuget. When this DI code is called from .net461 it works fine but when used in net core 6, I get an error stating it's unable to resolve type string and DateTimeOffset. I tried to inject a dummy string and the string error vanished but the struct DateTimeOffset persisted. Does .net6 interpret this DI in a different way?
If your classes in your nuget package tries to resolve this IAuthToken instance then you can add the following registration to your main app to override the registration that comes from the nuget. The registration order is important, so be sure to execute service registration from your nuget first.
services.AddTransient<IAuthToken, AccessToken>(
serviceProvider =>
{
// You can resolve any service that can provide the token and validTo parameters.
var tokenInfoProvider = serviceProvider.GetRequiredService<...>();
// Get the parameter values.
// Either from the tokenInfoProvider or specify it directly here.
string token = String.Empty;
DateTimeOffset validTo = DateTimeOffset.UtcNow.AddHours(1);
// Create and return the access token instance.
return new AccessToken(token, validTo);
}
);
If only the app tries to resolve an IAuthToken then I would suggest to create a factory (e.g. IAccessTokenFactory) that you can use to create the AccessToken instance with specific ctor parameters dynamically. Then register its implementation and inject IAccessTokenFactory where you need to get a new IAuthToken instance. So that you can keep your nuget code as it is now.
public interface IAccessTokenFactory
{
IAuthToken Create(string token, DateTimeOffset validTo);
}
public class AccessTokenFactory : IAccessTokenFactory
{
public IAuthToken Create(string token, DateTimeOffset validTo)
{
return new AccessToken(token, validTo);
}
}
// register this instead of the AccessToken
services.AddSingleton<IAccessTokenFactory, AccessTokenFactory>();
I have a project that written ASP.NET Boilerplate (assembly version=4.0.2.0).
I want get current transcation object in Application layer. How can I achieve this?
You get current unit of work with using IUnitOfWorkManager.
IUnitOfWorkManager _unitorWorkManager;
//...
[UnitOfWork]
public void Test(){
/*
Your code
*/
_unitOfWorkManager.Current//gives you current unit of work
.SaveChanges();//same thing with transaction.Commit();
}
If your class inherits something like AbpController, BackgroundJob, AbpServiceBase etc..., you can also use CurrentUnitOfWork.
//...
[UnitOfWork]
public void Test(){
/*
Your code
*/
CurrentUnitOfWork.SaveChanges();//same thing with transaction.Commit();
}
You can check https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work for more information.
Edit: I guess it is not possible to get it in application layer directly since it need dbcontext parameter. What about creating a domain service which provides ActiveDbTransaction. You can create an interface for that in *.Core project and define it where you can access to dbcontext
public interface IMyDbContextActiveTransactionProvider
{
/// <summary>
/// Gets the active transaction or null if current UOW is not transactional.
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
IDbTransaction GetActiveTransaction(ActiveTransactionProviderArgs args);
}
Implement it someplace you can access to dbcontext
public class MyDbContextActiveTransactionProvider: IMyDbContextActiveTransactionProvider, ITransientDependency {
private readonly IActiveTransactionProvider _transactionProvider;
public MyDbContextActiveTransactionProvider(IActiveTransactionProvider transactionProvider){
_transactionProvider = transactionProvider;
}
IDbTransaction GetActiveTransaction(ActiveTransactionProviderArgs args){
return _transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
{
{"ContextType", typeof(MyDbContext) },
{"MultiTenancySide", MultiTenancySide }
});
}
}
https://aspnetboilerplate.com/Pages/Documents/Articles/Using-Stored-Procedures,-User-Defined-Functions-and-Views/index.html#DocHelperMethods
I have a lot of services that require access to the current user.
I set up a PrincipalProvider which bound to the current HttpContext, I also registered it per request so that once instantiated, it would never lose the context.
It looks like this:
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly HttpContext _current;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider()
{
_current = HttpContext.Current;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _current?.User;
}
And I have bound it like this:
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
Now, any service that has this injected into it should have the user exposed. The issue I have is simple. If I have a controller set up like this:
public OrdersController(IOrderProvider provider)
{
_provider = provider;
}
That IOrderProvider has the IPrincipleProvider lazily injected into it, so in this case the user is available because the parent service is created on the Controller. The issue is if it isn't injected into the service, but instead used on another service that is injected into it.
For example, I have this controller:
public UsersController(IUserProvider provider, IAdvancedEncryptionStandardProvider encyptionProvider, IPhotoManager photoManager)
{
_userProvider = provider;
_encryptionProvider = encyptionProvider;
_photoManager = photoManager;
}
The IUserProvider doesn't have the IPrincipalProvider injected into it, but it does have another service that does:
public TroposSessionRequest(CormarConfig config, IAdvancedEncryptionStandardProvider encryptionProvider, Lazy<IPrincipalProvider> principalProvider)
{
_config = config;
_encryptionProvider = encryptionProvider;
_principalProvider = principalProvider;
}
The problem here, is that when the TroposSessionRequest is created, the HttpContext is not available and therefor is null.
I was hoping there was a way to instantiate the IPrincipleProvider when the context becomes available and then keep it for the entire request.
We can do this for every request as the PrincipalProvider is almost always needed.
Does anyone know how to do this?
The simplest way to workaround the problem is to simply access HttpContext.Current in your User accessor:
public IPrincipal User => HttpContext.Current?.User;
If you're concerned about testability, you can mock HttpContext.Current.User relatively easily.
If you're set on solving it using DI you could abstract HttpContext behind a IHttpContextAccessor dependency (but since you're still depending on the HttpContext type you'd still be in the same boat in terms of testing).
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly IHttpContextAccessor _httpContextAccessor;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _httpContextAccessor.HttpContext?.User;
}
I am using Autofac in my project and for the most part it works fine. A while ago, I needed to get access to the current user and was told the best way to do this was to create a wrapper class like this:
public class PrincipalProvider : IPrincipalProvider
{
public IPrincipal User => HttpContext.Current?.User;
}
This has worked without any issues through my application.
I now have a new provider which looks like this:
public class WmsProvider : IWmsProvider
{
private readonly Lazy<ILogProvider> _logProvider;
private readonly Lazy<IMessageProvider> _messageProvider;
private readonly CormarConfig _config;
private readonly ClaimsIdentity _identity;
public WmsProvider(IPrincipalProvider principalProvider, Lazy<ILogProvider> logProvider, Lazy<IMessageProvider> messageProvider)
{
_messageProvider = messageProvider;
_logProvider = logProvider;
_identity = (ClaimsIdentity)principalProvider.User.Identity;
}
/// <summary>
/// Sends the order to WMS
/// </summary>
/// <param name="model">The order model</param>
public async Task SendAsync(OrderViewModel model)
{
var request = WmsFactory.Create(model);
await _logProvider.Value.TraceAsync($"This is a test", _identity);
await _messageProvider.Value.CreateAsync(request, model.OrderNumber, MessageType.Wms, "ORD", Method.POST, null);
}
}
(I have stripped out the rest of the code for brevity)
In this case, the User is null and throws an error (Object instance not found).
But I have another class with a similar constructor:
public OrderProvider(CormarConfig config, IOrderService orderSerivce, IPrincipalProvider principalProvider, Lazy<IAccountProvider> accountProvider, Lazy<ICollectionManagerProvider> collectionManagerProvider, Lazy<IEmailProvider> emailProvider, Lazy<IJournalProvider> journalProvider, Lazy<IOrderLineProvider> orderLineProvider, Lazy<IStockProvider> stockProvider, Lazy<webServices> webService, Lazy<ITroposOrderLineService> troposOrderLineService, Lazy<ITroposOrderService> troposOrderService, Lazy<ITroposUnitOfWork> troposUnitOfWork, Lazy<IWmsProvider> wmsProvider)
{
//Assign our config
_config = config;
// Add our services to our class
_connectionType = config.ConnectionType;
_orderService = orderSerivce;
// Add our providers to our class
_identity = (ClaimsIdentity)principalProvider.User.Identity;
// Add our optional providers
_accountProvider = accountProvider;
_collectionManagerProvider = collectionManagerProvider;
_emailProvider = emailProvider;
_journalProvider = journalProvider;
_orderLineProvider = orderLineProvider;
_stockProvider = stockProvider;
_webService = webService;
_wmsProvider = wmsProvider;
_troposOrderLineService = troposOrderLineService;
_troposOrderService = troposOrderService;
_troposUnitOfWork = troposUnitOfWork;
}
And this works fine.
Both are registered the same way in my Module:
builder.RegisterType<OrderProvider>().As<IOrderProvider>().InstancePerRequest();
builder.RegisterType<WmsProvider>().As<IWmsProvider>().InstancePerRequest();
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>();
One thing to note, is that WmsProvider is injected into OrderProvider, so it is not directly injected into the controller. The controller constructor looks like this:
public OrdersController(IOrderProvider provider)
{
_provider = provider;
}
This might be where the issue lies. In a dependent, is the context not available? If it isn't what is the solution? Is there a way to get to the context from the child?
Any help would be appreciated.
I figured this out. It was as I said, the context was not available in the nested classes, so to fix this I changed the registration of the PrincipalProvider to instance per request:
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
And I changed the PrincipalProvider to look like this:
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly HttpContext _current;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider()
{
_current = HttpContext.Current;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _current?.User;
}
And this fixed my issue.
I am using EF6 with MySQL and have a Model that I will use for MULTIPLE Databases.
I would like to be able to set the connections settings in my Form.
How do I set the Connection String for my Model programatically?
you should use EntityConnectionFactory
Here is what you need.
public string CreateConnectionString(string BasicConnectionString)
{
//EntityConnectionFactory
var entityConnectionStringBuilder= new EntityConnectionStringBuilder();
entityConnectionStringBuilder.Provider = "Your Provicer here" //For me it is "System.Data.SqlClient";
entityConnectionStringBuilder.ProviderConnectionString = BasicConnectionString;
entityConnectionStringBuilder.Metadata = "res://*";
return entityConnectionStringBuilder.ToString();
}
Here is an sample usage
MyContext ctx = new MyContext(CreateConnectionString())
:: Update ::
As you are using DB first method, see the following image
when these two radio buttons are available, select the first one. Then you will be able to set the connection string of your model.
Here is how my context looks like (Though it is object context. But doesn't matter in context of this question)
public partial class DataContext : ObjectContext
{
#region Constructors
/// <summary>
/// Initializes a new DataContext object using the connection string found in the 'DataContext' section of the application configuration file.
/// </summary>
public DataContext() : base("name=DataContext", "DataContext")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new DataContext object.
/// </summary>
public DataContext(string connectionString) : base(connectionString, "DataContext")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new DataContext object.
/// </summary>
public DataContext(EntityConnection connection) : base(connection, "DataContext")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
#endregion
#region Partial Methods
partial void OnContextCreated();
#endregion
...
}
Update
Add the constructor you are looking for in a partial class outside of the auto-generated entity class:
public partial class WMSChennaiDEVEntities : DbContext
{
public WMSChennaiDEVEntities(string connectionstring)
: base(connectionstring)
{
}
}
This constructor is not included in EF 5/6 apparently to prevent us from accidentally passing a sql connection string when an entity connection string is desired.
you have to hardcode the connection string somewhere. The common place is app.config.
<connectionStrings>
<add name="Connection1" connectionString="Server=localhost\ServerInstance;Database=MyDB;Trusted_Connection=True;" providerName="System.Data.SqlClient" />
Then in your Code First model, do the following:
public class MyContext : DbContext
{
public MyContext():base("Connection1")
{...}
You see that BCL db library and EF were all designed for such usage pattern.
Changing connection strings in UI is not desired in business applications since users won't change db location very often, unless you are developing a DB admin app or an installer.