Azure Search .NET SDK: SearchIndexClient & Dependency Injection - azure

What's the recommended way to register a SearchIndexClient into DI container? (let's say we only have a single index)
Register it as singleton or transient?

The short answer is that you should register it as a singleton, as long as you make sure to avoid using properties and methods that aren't thread-safe. Most of them are thread-safe; just avoid setting mutable properties from multiple threads and you should be fine.
For a more in-depth discussion of why this is the recommended practice and how you can extend it for more complex scenarios, see this related question.

In an ASP.NET Core app, you can inject the client as follows.
Install the packages: Microsoft.Extensions.Azure, Azure.Search.Documents (this is the latest)
In the ConfigureServices method in startup.cs, register the client:
using Microsoft.Extensions.Azure;
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureClients(builder =>
{
builder.AddSearchIndexClient(new
Uri("my resource url"), new
AzureKeyCredential("my resource key"));
});
services.AddControllers();
}
Now say you want to use the client in your controller, you can inject it like so:
public class MyApiController : ControllerBase
{
private readonly SearchIndexClient _searchIndexClient;
public MyApiController(SearchIndexClient searchIndexClient)
{
_searchIndexClient = searchIndexClient;
}
}
You may not want to put your credentials directly in ConfigureServices though, in which case you could store them in appsettings.Development.json (or appsettings.Production.json):
"SearchDocument": {
"endpoint": "my resource url",
"credential": { "key": "my resource key" }
}
and do this in ConfigureServices:
services.AddAzureClients(builder =>
{
builder.AddSearchIndexClient(
Configuration.GetSection("SearchDocument"));
});
Read more about Dependency injection with the Azure SDK for .NET.

Related

asp.net core identity simpleinjector register applicationusermanager

i'm try isolation ASP.NET Core Identity version 1.1.2, architecture DDD and creating a CrossCutting layer to create a classlib for aspnet core identity , and i using SimpleInjector 4.0.8 for my IoC, so i create a class ApplicationUserManager and ApplicationSignInManager, but i'm cannot register this class in the simlpleinjector container
ApplicationUserManager
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
// My configurations stuffs were...
}
}
and the class ApplicationSignInManager
public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
public ApplicationSignInManager(UserManager<ApplicationUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<ApplicationUser>> logger)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
{ }
// TODO: bug com tipo de retorno IdentityResult para ClaimsPrincipal
//public override Task<ClaimsPrincipal> CreateUserPrincipalAsync(ApplicationUser user)
//{
// return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
//}
}
and i try register this class in BootStrapper.cs like this
public static void RegisterServices(Container container)
{
// Infra.Data App Context
// IdentityAppDbContext
container.RegisterSingleton(() =>
{
var options = new DbContextOptions<IdentityAppDbContext>();
return new IdentityAppDbContext(options);
});
// NetCore Identity
container.RegisterSingleton<ApplicationUserManager>();
container.RegisterSingleton<ApplicationSignInManager>();
container.RegisterSingleton<IUserStore<ApplicationUser>>(() =>
{
var options = new DbContextOptions<IdentityAppDbContext>();
return new UserStore<ApplicationUser>(new IdentityAppDbContext(options));
});
container.Register(() => (IOptions<IdentityOptions>)new IdentityOptions());
container.RegisterSingleton<IPasswordHasher<ApplicationUser>>(() => new PasswordHasher<ApplicationUser>());
}
but when i run the aaplication return erros says i need registre IOptions, IPasswordHasher and other params in contructors class, the queston is, how can'i register this class?
I had a similar requirement: my application needs to interact with identity (it's basically about human resources, that might have a user account on their own or not), and after struggling a long time with with various issues having identity in the framework container, I was trying to configure simple injector to provide all identity services. Call it crazy, but this is how it works (SimpleInjector 4.0 and ASP.Net Identity Core 2.0.1):
This is the "ConfigureServices" part:
// identity options are provided from outside, allowing configuration of the framework
container.RegisterSingleton<IOptions<IdentityOptions>>(new OptionsWrapper<IdentityOptions>(identityOptions));
// we rely on BCrypt instead of the default PBKDF2 hashing algorithm
container.Register<IPasswordHasher<MepUser>>(()=>new BCryptPasswordHasher(bcryptOptions));
// forwarding the framework logger to our own logging framework
container.Register<Microsoft.Extensions.Logging.ILoggerFactory, FrameworkToBackendFxLoggerFactory>();
container.Register(typeof(Microsoft.Extensions.Logging.ILogger<>), typeof(Microsoft.Extensions.Logging.Logger<>));
// identity store = a specific Entity Framework Core DbContext, getting mapped into a specific db scheme
container.RegisterSingleton(identityDbContextOptions);
container.Register<MepIdentityDbContext>();
// UserStore<T> und RoleStore<T> both require a DbContext (no IdentityDbContext, neither a generic TDbContext)
// via constructor, but the container only knows about MepIdentityDbContext so we have to wire it manually
container.Register<IUserStore<MepUser>>(() => new UserStore<MepUser>(
container.GetInstance<MepIdentityDbContext>(),
container.GetInstance<IdentityErrorDescriber>()));
container.Register<IRoleStore<IdentityRole>>(() => new RoleStore<IdentityRole>(
container.GetInstance<MepIdentityDbContext>(),
container.GetInstance<IdentityErrorDescriber>()));
// framework internal services
container.Register<IdentityErrorDescriber>();
container.Register<ILookupNormalizer, UpperInvariantLookupNormalizer>();
container.Register<IPasswordValidator<MepUser>, PasswordValidator<MepUser>>();
container.Register<IUserValidator<MepUser>, UserValidator<MepUser>>();
container.Register<IUserClaimsPrincipalFactory<MepUser>, UserClaimsPrincipalFactory<MepUser>>();
container.Register<IRoleValidator<IdentityRole>, RoleValidator<IdentityRole>>();
// ASP.Net Core Identity violates a design decision of SimpleInjector: The API clearly differentiates the registration of collections
// https://simpleinjector.readthedocs.io/en/latest/decisions.html#the-api-clearly-differentiates-the-registration-of-collections
// By registering IEnumerables of those violating services with a simple wrapping single item array, Identity is happy
container.Register<IEnumerable<IUserValidator<MepUser>>>(() => new[] { container.GetInstance<IUserValidator<MepUser>>() });
container.Register<IEnumerable<IRoleValidator<IdentityRole>>>(() => new[] { container.GetInstance<IRoleValidator<IdentityRole>>() });
container.Register<IEnumerable<IPasswordValidator<MepUser>>>(() => new[] { container.GetInstance<IPasswordValidator<MepUser>>() });
// Role and UserManager reflect the API surface of the whole ASP.Net Core Identity framework
container.Register<RoleManager<IdentityRole>>();
// UserManagerShim is omitting (=nulling) the IServiceProvider parameter of UserManager<T>
container.Register<UserManager<MepUser>, UserManagerShim>();
and this needs to be done during "Configure", otherwise password reset and other two factor token stuff won't work (runtime error, although the container was verified)
// if you eagerly instantiate a provider instance that is considered being a singleton and
// put it into the respective field in the TokenProviderDescriptor and list it in the option's
// provider map, ASP.Net Core Identity will use this one instead of asking the IServiceProvider
// instance injected into UserManager<T> (that we do not do, because it is bad design. Instead,
// we just stuff null in there)
identityOptions.Tokens.ProviderMap[TokenOptions.DefaultProvider] =
new TokenProviderDescriptor(typeof(DataProtectorTokenProvider<MepUser>))
{
ProviderInstance = new DataProtectorTokenProvider<MepUser>(
dataProtectionProvider,
new OptionsWrapper<DataProtectionTokenProviderOptions>(new DataProtectionTokenProviderOptions()))
};
identityOptions.Tokens.ProviderMap[TokenOptions.DefaultEmailProvider] =
new TokenProviderDescriptor(typeof(EmailTokenProvider<MepUser>)) {
ProviderInstance = new EmailTokenProvider<MepUser>()
};
Although there appears AspNetCore in the namespace definition, nothing is dependent on ASP.Net Core hosting, actually. The only dependency to the "outside world" are the options (POCOs) and an implementation of IDataProtectionProvider, that will use the KeyRing in ASP.Net scenarios, but can also be satisfied using the EphemeralDataProtectionProvider in tests.
Caveat: SignInManager<T> is not being injected. This class is a total mess, depending on the whole world, so I rewrote it basically matching my requirements. You also loose lots of the configuration flexibility regarding token providers. But however, in my case it's something you decide once at design time, so I am fine with it.

How to make app settings in .NET Core that translate to Azure app settings?

I can't remember where I saw this but I followed the advice on a blog when setting up my app configuration for my .NET Core MVC application. I created a model like this to hold some settings my app needed:
public class BasePathSettings
{
public string BaseImageFolder { get; set; }
public string BaseApiUrl { get; set; }
}
My StartUp has this...
public void ConfigureServices(IServiceCollection services)
{
...
// this adds the base paths to container
services.Configure<BasePathSettings>(Configuration.GetSection("BasePathSettings"));
....
}
And the appsettings.json has this in it:
"BasePathSettings": {
"BaseImageFolder": "D:\\Images\\",
"BaseApiUrl": "http://localhost:50321/"
},
I inject the controllers that need this info like so....
private readonly BasePathSettings _settings;
public ClientsController(IOptions<BasePathSettings> settings)
{
_settings = settings.Value;
_client = new HttpClient();
_client.BaseAddress = new Uri(_settings.BaseApiUrl);
}
Running this on my localhost everything runs fine.
However, when I deploy this application to Azure I assumed I needed to create an application setting in the General Settings of the app service. So I made an app setting called BasePathSettings and copied the json for the setting into the value:
{ "BaseImageFolder": "imagePath", "BaseApiUrl": "apiUrl" }
It appears that Azure barfs when it's in the ConfigureServices code claiming that the web.config does not have the correct permissions in NTFS. I'm guessing the real culprit is how the json value is being read from the Azure application settings.
Can I even use json there? If so, does it need formatted differently?
Can I even use json there? If so, does it need formatted differently?
To add hierarchical structure settings to Azure web app, we could place a colon between the section name and the key name. For example,
use BasePathSettings:BaseImageFolder to set your folder
use BasePathSettings:BaseApiUrl to set your url
I made an app setting called BasePathSettings and copied the json for the setting into the value
Format should be -
basepathsettings:baseimagefolder (just single slash)
basepathsettings:baseapiurl
If you try to define "BasePathSettings" in a single WebApp setting that takes a json value, the GetSection will return null.
As a workarround, I use this extension method as a replacement of GetSection() :
public static T GetWebAppSection<T>(this IConfiguration config, string key)
where T:class
{
T configValue = config.GetSection(key).Get<T>();
if(configValue == default(T))
{
configValue = JsonConvert.DeserializeObject<T>(config[key]);
}
return configValue;
}

How do I apply a custom ServiceStack RequestFilterAttribute to an auto-generated Service?

I have a custom RequestFilterAttribute that I am applying to my ServiceStack services:
[MyCustomAttribute]
public class MyService : ServiceStack.Service {...
I have recently begun using the AutoQuery feature (which is awesome) but I'm wondering how to apply MyCustomAttribute to the auto-generated services that you "get for free" when your request DTO inherits from QueryBase.
I could certainly add methods to my service with the "magic" AutoQuery code:
SqlExpression<DTO> sqlExpression = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<DTO> response = AutoQuery.Execute(request, sqlExpression);
but I'm hoping there's a better way?
If you wanted to customize the AutoQuery behavior you should first take a look at the extensibility options using Query Filters provides.
Otherwise you should be able to add the RequestFilter Attribute to the Request DTO itself, i.e:
[MyCustomAttribute]
public class MyQuery : QueryBase<Poco> {}
Alternatively you can get a reference to the auto-generated Service using:
var autoQueryService = appHost.Metadata.GetServiceTypeByRequest(typeof(MyQuery));
And then use the dynamic API to add custom attributes to it, e.g:
autoQueryService
.AddAttributes(new MyCustomAttribute { ... });
Since the Services are only generated and registered once the AutoQueryFeature Plugin is executed you'll only be able to access the service after all plugins are loaded which you can do:
1) In your own plugin by implementing the IPostInitPlugin Interface
2) By registering a AfterInitCallbacks handler:
this.AfterInitCallbacks.Add(appHost => { ... });
3) By overriding OnAfterInit() virtual method in your AppHost, e.g:
public override void OnAfterInit()
{
...
base.OnAfterInit();
}

Extending Service/IService to add common dependencies

I have the need to extend Service/IService to allow me to register additional resources like other DB connections and custom classes that each individual service may need to get a handle to.
Is the proper way to do this to subclass Service? Also, it is not clear to me if I have another (say) IDbConnection how Funq figures out which Property to inject the value into.
If you have multiple services with the same type you need to register them in funq with a name. Unfortunatly I don't think funq can autowire the properties correctly so you need to manually resolve them.
container.Register<DataContext>("Security", x => new SecurityDataContext());
container.Register<DataContext>("Customers", x => new CustomersDataContext());
container.Register<DataContext>("Reporting", x => new ReportingDataContext());
container.Register<IReportRepository>(x => new ReportRepositoryImpl(x.ResolveNamed<DataContext>("Reporting")));
An alternative approach would be to create a unique interface (even if it has no members) for each type and then use that in funq. This would allow autowiring
container.Register<ISecurityDataContext>(x => new SecurityDataContext());
container.Register<ICustomersDataContext>(x => new CustomersDataContext());
container.Register<IReportingDataContext>(x => new ReportingDataContext());
// this could just be autowired
container.Register<IReportRepository>(x => new ReportRepositoryImpl(x.Resolve<IReportingDataContext>()));
If you still really need to extend Service you can just use standard inheritance in c#
public abstract class BaseService : Service
{
// custom things go here
public string Example() {
return "Hello World";
}
}
public class ReportsService : BaseService
{
public string Get(ListReports request) {
return Example();
}
}
You can configure other DB connections easily without extending the Service , but by just wiring them in the configure method in the AppHost.cs file.

Castle Windsor Factory implementation

I am using a Typed Factory supplied by Windsor Castle DI container. I am quite new to DI containers, so am after a bit of guidance with my solution.
Here is my implementation:
*updated registration & DB call
public interface IAgent { }
public class Agent : IAgent { }
public interface IAgentFactory
{
IAgent Create();
IAgent Create(int agentId);
IAgent Create(AgentDTO agentDTO);
}
class AgentFactory : IAgentFactory
{
public IAgent Create()
{
return InitNewEntity(new Agent());
}
public IAgent Create(int agentId, IDBContext dbContext) //Not happy with this, as it's a dependency that isn't factored out :(
{
return dbContext.GetAgent(agentId);
}
public IAgent Create(AgentDTO agentDTO)
{
Agent agent = InitNewEntity(new Agent());
agent.ParseDTO(agentDTO);
return agent;
}
private IAgent InitNewEntity(IAgent agent)
{
agent.Username = ""; /// + other fields to initialise
agent.DOB = DateTime.Now; /// etc.
return agent;
}
...
Container.AddFacility<TypedFactoryFacility>()
.Register( Component.For<IAgentFactory>()
.ImplementedBy<AgentFactory>());
which I'm using the following call to get some entities
IAgentFactory agentFactory = ViewModel.Container.Resolve<IAgentFactory>();
IAgent agent = agentFactory.Create(); //Creates new Agent entity
agent = agentFactory.Create(66, dbContext); //Looks up entity in database, don't like passing in a db context, another dependency
agent = agentFactory.Create(agentDTO); //Creates Agent entity from DTO object
I have several concerns about this.
There are 3 possible scenarios regarding creating a new Agent,
1: Create a new agent from scratch
2: Create an agent using an existing DTO (Data Transfer Object)
3: Create an agent with a call to the database.
I decided to put all this functionality into a factory, as it seems to fit the bill, however, I am not entirely sure if this is the correct or best way to accomplish this.
The question is, is it possible to leverage all 3 Create scenarios into the DI container Register statement and omit the concrete factory implementation altogether, so that I no longer have an AgentFactory class, but only the IAgentFactory interface.
Another question I have regarding this implementation, is it possible to do a call, such that if I request an Agent entity, Windsor will first make a call to the factory and return the entity created from the factory, rather than create the entity without referencing the factory.
ie.
IAgent agent = Container.Resolve<IAgent>(66);
With a Callstack:
Container -> AgentFactory -> Create(66) -> returns agent from factory.
Which Windsor will intercept and automatically use the factory to create the instance. However, I tried this and it didn't use the factory to create it, it just went ahead and created the Agent without referencing the factory.
I did have this, but it doesn't call the factory methods that I have defined.
Container.AddFacility<TypedFactoryFacility>()
.Register(Component.For<IAgentFactory>().AsFactory(),
Component.For<IAgent>().ImplementedBy<Agent>());
Advice is appreciated, thank you.
Typed Factory is designed to create "behaviour" instance, not "data" instance.
You do not register into the container a "model" component, but compenent to work w/ the model.
In other words you register into the container everything but the model.
You AgentFactory has to be registerted into the container, but that's not a "Typed Factory".
You may use TF for "late dependency" purpose.
I prefer be more decoupled w/ my design and also more "single responsability" oriented.
AgentFactory won't hide a repository (as per your Create from db) within the factory: I will pass a datareader as Create parameter instead.

Resources