How to config an instance (folder) inside redis database - c#

I am using c# with StackExchange.Redis.
I used something like this to connect :
var client = ConnectionMultiplexer.Connect(_appSettings["Cache:Redis:Configuration"]);
_database = client.GetDatabase(int.Parse(_appSettings["Cache:Redis:LockProcessDB"]));
and then I access the set / get commands via :
_database.StringSet(data.Key, data.Value, TimeSpan.FromSeconds(_ttl), When.NotExists, CommandFlags.None);
...
I now need to divide different "sections" in my data into different "folders" in the redis database.
So instead of just setting values under DB 7, I want DB 7 to have a sub folder called : Documents and the values I am setting should be inserted under that folder.
In the Microsoft.Extensions.Caching.Distributed for .net core solution they support in registering it like this :
services.AddDistributedRedisCache(options =>
{
options.Configuration = ConfigUtils.Instance.Get("Redis_Config", "");
options.InstanceName = "Documents:";
});
which then directs my set commands into the "Documents" sub folder.
How do I achieve that with StackExchange.Redis?

It seems that I didnt understand an important part about separation in a Redis DB.
The option for a prefix and the way to use it.
When trying to set a value I can actually add a prefix like -
string _prefix = "documents:";
_database.StringSet(_prefix + data.Key, data.Value, TimeSpan.FromSeconds(_ttl), When.NotExists, CommandFlags.None);
Which achieves the desired result.
The data saves in my redis DB as documents:myKey under a folder named "documents"

Related

C# AWS Parameter Store - Configuration not loading SystemManagerConfiguration in .Net 6

Application is not able to talk to AWS Parameter Store in .NET 6.
It is always talking to appsettings.json.
I tried debugging locally, still same behavior. Not able to find the SystemManagerConfiguration under list of configuration .
var builder = WebApplication.CreateBuilder();
var connectionString = builder.Configuration.GetConnectionString("OrderTrackerDatabase");
Packages Used
Library Source Code : https://github.com/aws/aws-dotnet-extensions-configuration
image
I got the same issue and finally resolved it.
The samples code in https://github.com/aws/aws-dotnet-extensions-configuration missed one line as below after called "AddSystemsManager" method in .Net 6.
builder.Services.Configure<Settings>(builder.Configuration.GetSection($"common:settings"));
After added above line, then I'm able to get the correct values from AWS Parameter Store when using the settings.
I've also created an issue of this in GitHub as below -
https://github.com/aws/aws-dotnet-extensions-configuration/issues/114
I believe the problem might be the trailing slash after "/OrderTracking/", try "/OrderTracking" instead.
WebApplication.CreateBuilder() will create new instance and doesn't carry over the SystemManager configuration.
Instead, use IConfiguration instance through constructor DI.
var connectionString = _configuration.GetConnectionString("OrderTrackerDatabase");
In my case this extensions method was returning null at my lambda:
private static IConfiguration InitializeConfiguration() => new ConfigurationBuilder()
.AddSystemsManager($"/my-data", true, TimeSpan.FromMinutes(5))
.Build();
Because the role of lambda didn't have permission for read SSM for that resource.
User: is not authorized to perform: ssm:GetParametersByPath on resource
So, just add necessary permission (ssm:GetParametersByPath) for the role of lambda to your resource at System Manager.
In my case, I am using lambda serverless, so the IConfig is always null when it is passed to the controller.
I resolved it by changing the IOptions<Settings> settings in the Controller constructor to IConfiguration settings and then access the parameters by name like _settings.GetValue<string>("ParameterName")
A little less "Object Oriented", but it seemed much easier than this complex solution

Is it possible to persist an environment setting from within an Azure function?

I've created an Azure Function that retrieves new form inputs from a website, processes them and stores the result in another system by using an API call. I only want to retrieve the form inputs that have not been processed before. This is supported by the website.
I'm reading the timestamp of the most recent form input that has already been processed. This works fine.
I'm using the following function to read the setting from the Azure function environment:
private static string GetEnvironmentVariable(string name)
{
return System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}
After I've processed a form input, I store the timestamp of the form with the following function:
private static void SetEnvironmentVariable(string name, string value)
{
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process);
}
Everything seems to be working fine. I see in the logs that form inputs don't get processed more than once. However, when I take a look at the environment variables in the Azure dashboard, I can see that the initial value of the variable is still present. This initial value will be used when the environment 'shuts down' and is restarted (e.g. after changing the value of another environment variable).
I've tried to change the target from 'Process' to 'Machine', but this results in access control errors. There are some questions on SO that are related to my issue, but none of them provides me with an answer for my situation.
I would like to know whether:
Environment variables are the / a suited solution for my use case;
If so, how can I prevent that a variable will be reset to its initial value after resetting the Azure environment.
Thanks in advance!
Firstly, the Environment.SetEnvironmentVariable method already worked in your case.
Here is an answer from Hury Shen:
When you set the variable by Environment.SetEnvironmentVariable, it
will not show in application setting. But we can use it by
Environment.GetEnvironmentVariable as expected. Although the solution
you mentioned is not so good, but it can implement your requirement.
The adverse effect is when you restart the function app, the variables
will be lost.
About the target Machine: The environment variable is stored or retrieved from the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment key in the Windows operating system registry. This value should be used on .NET implementations running on Windows systems only.
One way to achieve but not set inside code:
In App Service, you can set app settings outside of your app code.
Then you can access them in any class using the standard ASP.NET Core
dependency injection pattern:
using Microsoft.Extensions.Configuration;
namespace SomeNamespace
{
public class SomeClass
{
private IConfiguration _configuration;
public SomeClass(IConfiguration configuration)
{
_configuration = configuration;
}
public SomeMethod()
{
// retrieve nested App Service app setting
var myHierarchicalConfig = _configuration["My:Hierarchical:Config:Data"];
// retrieve App Service connection string
var myConnString = _configuration.GetConnectionString("MyDbConnection");
}
}
}

How to set the SecurityStamp of a user in MVC5?

I have an MVC5, which uses ASP.NET Identity for users. I have a class named Business which inherits from ApplicationUser, then I populate the database with the entries in my CSV files, but then in the database they don't have a SecurityStamp and I cannot seem to be able to log in. I tried something like this in my Configuration.cs file, but it doesn't seem to work:
var userManager = new UserManager<Business>(new UserStore<Business>(context));
foreach (Business b in context.Businesses)
{
userManager.UpdateSecurityStampAsync(b.Id);
}
context.SaveChanges();
Please note that initially their SecurityStamp is null in the database. Any idea, how to add the security stamps from Configuration.cs?
You should always use the non-async versions of methods that are not intended to be awaited.
userManager.UpdateSecurityStamp(b.Id);
Change context.Businesses to context.Businesses.ToList()
The error you were getting There is already an open DataReader associated with this Connection which must be closed first. is probably because you are iterating a set which is streaming objects from your DB and at the same time trying to issue additional commands through UpdateSecurityStamp

How to keep personalization when moving class derived from WebPartManager to new assembly?

We have a web site project that was started many years ago. The landing page uses personalizable web parts, and the web part manager is an extension of the .NET WebPartManager class, residing in App_Code. The curious thing is that if we move this class into another assembly in the same solution, the personalization is cleared and we can save new personalization. But when we move the class back, it loads the original personalization again. The PathId, UserId, and ApplicationId all appear to stay the same in the asp_PersonalizationPerUser table (See http://msdn.microsoft.com/en-us/library/aa478955.aspx for details.)
UPDATE: The reason I ask is because we need to convert our project to a Web Application project, and we do not want to cause any disruption among some the 500 users that use the application daily. I have put a shelveset together for the conversion and everything seems to be worked out except for this problem.
UPDATE 2: It occurred to me that I need to re-word my question.
How can I keep personalization when moving our class that derives from WebPartManager to a new assembly?
Because the implementation is very complicated and internal to the .NET framework, creating a custom source code to manage this is unfeasible. Instead, running a database script during maintenance will be the best option. Example:
// Read the 'PageSettings' column from the ASP personalization tables
// into a byte array variable called 'rawData' first. Then continue:
var mems = new System.IO.MemoryStream(rawData);
var formatter = new System.Web.UI.ObjectStateFormatter();
var oldState = formatter.Deserialize(mems) as object[];
var index = oldState.ToList()
.FindIndex(o =>
o as Type != null &&
((Type)o).Name.Contains("WebPartManager"));
// In our case, the derivative class name is "MyWebPartManager", you
// may need to change that to meet your requirements
oldState[index] = typeof(WebPartManager);
mems = new System.IO.MemoryStream();
formatter.Serialize(mems, oldState);
var newState = mems.ToArray();
// Write 'newState' back into the database.
In addition to scoping personalization state by user name and path,
SqlPersonalizationProvider supports scoping by application name.
Websites that register personalization providers with identical
applicationName attributes share Web Parts personalization data,
whereas websites that register personalization providers with unique
applicationNames do not. Due to the page-specific and control-specific
nature of personalization data, however, it usually doesn't make sense
to use the same applicationName for Web Parts personalization data
across different websites.
Does your new Web Application project have a different applicationName than the old one?

Easiest way to implement this dynamic login scheme for asp.net

I currently have a site where different users can login, and depending on their sub domain, are presented with different data from different databases. I am overriding the SqlMembershipProvider to use a "temp" connection string, that I dynamically set during the Initialize Method, per this post's instructions:
http://forums.asp.net/p/997608/2209437.aspx
public override void Initialize(string name, NameValueCollection config)
{
// intercept the setting of the connection string so that we can set it ourselves...
string specifiedConnectionString = config["connectionStringName"];
ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings[specifiedConnectionString];
var fi = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
fi.SetValue(connectionString, false);
connectionString.ConnectionString = WakeflyClientHelper.GetClientConnectionStringByCurrentSubdomain();
config["connectionStringName"] = connectionString.Name;
// Pass doctored config to base classes
base.Initialize(name, config);
}
The problem is that the SqlMembershipProvider class seems "static" in that when multiple users connect from different sub domains, they end up seeing the User Accounts from ONE of the databases, not each of their own. It looks like the Initialize is called by the application, and not on a per request basis.
So my question to you is... What would be the easiest way to implement a solution to this?
I have not written a custom provider before, so I'm not sure how that works, or what the limitations are. And if I write it myself, there is always the possibility of security holes if I overlook something (not to mention the time it will take). Any ideas?
Update 1:
I could have a single database, however, the user's within a given sub domain need the ability to add/edit/delete user's within their own sub domain, without seeing other sub domains' users. Would that be possible under this scheme?
The code for the SQLMembershipProvider is provided here. You could extend/change what you think is necessary by adding the project to your solution and changing the namespace references in web.config.
I have done exactly this using a single membership database to handle all the logins, and then hook them up to the appropriate database depending on the domain in which they logged in.

Categories