I'm currently having some configuration variables in my Web.config, eg:
<appSettings>
<add key="RecipientEmailForNotifications" value="asdf#sdf.com" />
<add key="NotifyNewEntries" value="true" />
<!-- etc... -->
</appSettings>
I have a view where admin users can change this values online, eg.
/Admin/Configuration/
Recipient email for notifications:
[___________]
Notify new entries:
[x] Yes
[SAVE]
But there are two problems with this approach:
Each time I deploy the web.config, I'm overriding those values that were saved online
Each time the web application restarts (because web applications are naturally stopped when there is no activity), these values that were saved online are reset to the originals of the last deployment
So the question is:
What is the best way of achieving this goal of having configuration variables that have to be persisted?
I think that using a table isn't very natural, but I might be wrong (please explain why).
Also, using app settings is very convenient when reading the values:
ConfigurationManager.AppSettings["RecipientEmailForNotifications"]
As opposed of using the database,
db.SomeConfigurationTable.SingleOrDefault(.....)
I'd use a combination of database store and caching.
If you are on a single web server you could store the settings in proc cache. If you are in a web farm you could look at something like couchbase (free memcached) or azure.
It might make sense to warm your cache on applicaiton start. That is read the settings from your database and populate your cache.
Another popular approach is to have a Get call that check the cache first and populate cache if cache is empty, e.g.
public string Get(string key, Func<string> getItemCallback)
{
var item = Cache.Get(key) as string;
if (item == null)
{
item = db.SomeConfigurationTable.SingleOrDefault(key);
Cache.Store(key, item);
}
return item;
}
Not forgetting you will need to burst your cache when updating settings.
Related
I have some variable in my DataEntityLayer which are common for all the users for example
public class DataEntityLayer : System.Web.UI.Page
{
public string UserName
{
get
{
if (Session["UserName"] == null)
{
GotoLoginPage();
return null;
}
else
return Session["UserName"].ToString();
}
set { Session["UserName"] = value; }
}
}
I am using this variable to display Username when a user logs in, this is working fine but goes null frequently. How can I avoid that or let me know a way to declare a variable in my Layer to show Username of a user when he logs in.
Session is temporary memory that expires after a defined amount of time, I believe the default is 20 minutes of inactivity.
It could also be because you're using multiple servers with load balancing. Unless the session is persisted in a database then as you move from one server to another, the session is lost.
I would recommend avoiding this approach for the above reasons. There are better and more secure ways to handle authentication.
P.S An object called DataEntityLayer should not derive from System.Web.UI.Page!!
I totally agree with #johnMc in regards to the security, best use a library or package that gives a total solution. For completeness, the reason for the session being lost could be also running the service on a multi-threaded application. If using IIS, you can turn on a service called "ASP.Net state service" and then enable it in your web config. This does out-of-process session handling for ASP.net. There is also a way to connect to this service using non-IIS, but you'll have to build a communicator class to handle this. There is a small performance hit for this process, but I've never had any issues on high loads.
<system.web>
<sessionState mode="StateServer" />
</system.web>
(I would've commented, but I can't add a comment yet?) :)
I have a fairly basic MVC4 website. I'm using SQL Server sessions like so:
<sessionState
mode="SQLServer" regenerateExpiredSessionId="true" timeout="1440"
sqlConnectionString="Password=pass; Persist Security Info=True; User ID=user; Data Source=server;"
/>
When a user logs in, I add an item to the session like so:
Session["test"] = 3;
That works fine, the issue arises when I try to add new items to the session or update existing values, ie:
Session["test"] = 4;
Session["faux"] = 8;
As I'm debugging, the initial "test" value never gets updated, and the "faux" value is never added. No error is thrown, and if I step into/over the update/add code, the session looks like it's being updated, but if I step into another function and try to query the session, only the original values exist in the session.
I followed these instructions: MSDN, and the tables are showing up on my server.
I'm at a loss. Why would I be able to create a session, but never update/add values in that session?
The issue was that in one controller I had [SessionState(SessionStateBehavior.Required)] (which was the controller that was working properly), and in the other controller (the one that was not working properly), I had [SessionState(SessionStateBehavior.ReadOnly)] set, which is why my session items were never being updated. This was not an issue when my session state was InProc.
I'm keeping this question open in-case anyone else makes the same bad mistake that I did.
We are working on a legacy C# enterprise app. Its client uses several web services, whose URLs, among lots of other settings, are read from the local app.config file. We want to migrate these settings into a global DB to simplify their management. However, I can't figure out how (and whether) it is possible to migrate the web service URLs. These are read from the service client code generated by VS and I can't seem to find a way to tell VS to use a different settings provider than the one generated into Settings.Designer.cs .
We can overwrite the service facade's Url property with the value we want, after it is created - this is the solution currently used in several places in the code. However, I wouldn't like to touch every part of our codebase where any of these services is used (now and in the future). Even less would I like to modify generated code.
There has to be a better, cleaner, safer solution - or is there?
Btw our app runs on .NET 2.0 and we won't migrate to newer versions of the platform in the foreseeable future.
The Refernce.cs file that is generated by the Visual Studio indicates that the URL of the webservice will be retrieved from the settings:
this.Url = global::ConsoleApplication1.Properties.
Settings.Default.ConsoleApplication1_net_webservicex_www_BarCode;
I believe that John Saunders gave you a wonderful suggestion in his comment. You need a SettingsProvider class which:
...defines the mechanism for storing configuration data used in the
application settings architecture. The .NET Framework contains a
single default settings provider, LocalFileSettingsProvider, which
stores configuration data to the local file system. However, you can
create alternate storage mechanisms by deriving from the abstract
SettingsProvider class. The provider that a wrapper class uses is
determined by decorating the wrapper class with the
SettingsProviderAttribute. If this attribute is not provided, the
default, LocalFileSettingsProvider, is used.
I don't know how much you have progressed following this approach, but it should go pretty straighforward:
Create the SettingsProvider class:
namespace MySettings.Providers
{
Dictionary<string, object> _mySettings;
class MySettingsProvider : SettingsProvider
{
// Implement the constructor, override Name, Initialize,
// ApplicationName, SetPropertyValues and GetPropertyValues (see step 3 below)
//
// In the constructor, you probably might want to initialize the _mySettings
// dictionary and load the custom configuration into it.
// Probably you don't want make calls to the database each time
// you want to read a setting's value
}
}
Extend the class definition for the project's YourProjectName.Properties.Settings partial class and decorate it with the SettingsProviderAttribute:
[System.Configuration.SettingsProvider(typeof(MySettings.Providers.MySettingsProvider))]
internal sealed partial class Settings
{
//
}
In the overridden GetPropertyValues method, you have to get the mapped value from the _mySettings dictionary:
public override SettingsPropertyValueCollection GetPropertyValues(
SettingsContext context,
SettingsPropertyCollection collection)
{
var spvc = new SettingsPropertyValueCollection();
foreach (SettingsProperty item in collection)
{
var sp = new SettingsProperty(item);
var spv = new SettingsPropertyValue(item);
spv.SerializedValue = _mySettings[item.Name];
spv.PropertyValue = _mySettings[item.Name];
spvc.Add(spv);
}
return spvc;
}
As you can see in the code, in order to do that, you need to know the setting name as it was added in the app.config and the Settings.settings when you have added the reference to the web service (ConsoleApplication1_net_webservicex_www_BarCode):
<applicationSettings>
<ConsoleApplication30.Properties.Settings>
<setting name="ConsoleApplication1_net_webservicex_www_BarCode"
serializeAs="String">
<value>http://www.webservicex.net/genericbarcode.asmx</value>
</setting>
</ConsoleApplication30.Properties.Settings>
</applicationSettings>
This is a very simple example, but you might use a more complex object to store the configuration information in conjunction with other properties available in the context such as item.Attributes or context in order to get the proper configuration value.
First post, I'm a complete .Net/C# novice thrown in at the deep end!
I've inherited a C# wed application due to someone leaving at work and me being the only one with bandwidth! But not the .Net, C# knowledge!
The app is used by people on different sites all over the world. They log in using the corporate login details and as such they log into different servers depending on where they are located, (Europe, America or India).
The guy who wrote the app couldn't work out how to switch the ConnectionString in web.config depending on location, so duplicated the whole app for each domain! With the only variation being a single IP address in web.config for each duplicated version of the app! Then did a simple web front page which took the user to "their" version of the app depending on where they said they were in the world!
The first thing I want to do is to move to a single version to maintain, so I need to be able to switch the connection string or how to login?
I've spent several days trying to work out how I get to ConnectionString (defined in web.config) from my Login class, only to discover the values set in web.config seem to be read only, so I can't alter them.
So I guess the first question is, am I barking up the wrong tree? Can I just set all the information that AspNetActiveDirectoryMembershipProvider (see code later) requires and call it from my login class? Or is the ConnectionString route the Ipso facto way to set up connections in .Net/C#? So therefor I do need to find out how to change/specify/add the value at runtime.
Three possibilities I can think of:- (The first is the one I've ground to a hult with)
Change the ConnectionString for ADService in my web.config from my Login class?
Change what AspNetActiveDirectoryMembershipProvider uses, so from my Login class magicly get it to use EMEA_ADService or PACIFIC_ADService as defined in web.config?
Is it possible to define a new connectionString and call AspNetActiveDirectoryMembershipProvider all from my Login class, not using web.config for this connection at all?
Here's a bit of my/his web.config file and my Login class
Cuttings from Web.config
<connectionStrings>
<add name="ADService" connectionString="LDAP://12.345.67.8" /> *---- Original ConnectionString (IP address changed)----*
<add name="EMEA_ADService" connectionString="LDAP://12.345.67.8" /> *---- Added by me playing around unsuccessfully! ----*
<add name="PACIFIC_ADService" connectionString="LDAP://12.345.67.9" /> *---- Added by me playing around unsuccessfully! ----*
~
</connectionStrings>
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" timeout="2880" /> *---- The background class for this popup (Login.aspx.cs) is where I'm currently trying to affect ConnectionString----*
</authentication>
*---- Pretty sure this is the bit that actually does the login verification----*
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
<providers>
<clear />
<add name="AspNetActiveDirectoryMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=12345678" connectionStringName="ADService" applicationName="/." description="ADService" />
</providers>
</membership>
This is as far as I've got in my class before finding out that I don't appear to be able to alter ConnectionString!
Cuttings from Login.aspx.cs
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings; //this is now working :)
string userDomain = Environment.UserDomainName; //Probably don't need this, it seems to give the login domain on the machine. Don't know yet if that will be the users machine or the server the app runs on?
if (connections.Count != 0)
{
foreach (ConnectionStringSettings connection in connections)
{
string testname = connections["ADService"].Name;
string testConnectionString = connections["ADService"].ConnectionString;
connections["ADService"].ConnectionString = "LDAP://12.345.67.9";
testConnectionString = connections["ADService"].ConnectionString;
Any hint would be very welcome!
P.S. I have requested a .Net/C# course at work! ;)
You wouldn't want to alter the existing connection string. Rather, you'd want to alter which connection string your Data Access Layer was using to call different service stacks. You could then choose a connection string at runtime based on whatever input parameters you wanted to use. which in your case might be an IP range.
asp.net mvc multiple connection strings
Handling multiple connection strings in ONE DataAccess Layer
http://msdn.microsoft.com/en-us/library/aa479086.aspx
The microsoft article is particularly interesting since it actually takes an architectural look at proper patterns for resolving dilemmas like yours. I think you got stuck with the short end of the stick! Best of luck!
The Web.config cannot be modified at Runtime. I would suggest setting some kind of flag via a login link or combobox on the website for people to use to choose where they want to login. It is not the job of the server to figure out what a user wants to do.
I have followed a tutorial to implement LDAP (Active Directory) authentication to an ASP.NET/C# 4.0 Web Application. I have the authentication working, and am able to log in under a user of our domain. The next step however is not covered in this tutorial, where I need to keep a session-specific object with some variable data.
Now that I have LDAP authentication working, I'm making a class to wrap the session. However, I'm not sure how I can create this session in a way that it will stay active through all this user's requests. In Globals.asax, I have utilized Application_AuthenticateRequest as required in the tutorial. I'm assuming there's something I need to do here, but since I'm new to C# (more familiar with Delphi), I don't know where I need to actually declare/create this user class instance.
This class contains some things I'd like to keep accessible throughout this user's session, assuming of course the server will stay running throughout this time. For example, a dataset containing product data, which the user may request various parts of this same dataset in different requests. Therefore, it must stay accessible throughout the entire user's session, not just that single HTTP request.
I think you should have a look at Forms authentication for asp.net.
Web.config:
<authentication mode="Forms">
<forms name=".ASPXFORMSAUTH" loginUrl="/Login.aspx" timeout="120" />
</authentication>
<authorization>
<deny users="?" />
</authorization>
After you have managed to log in your visitor using ldap you can log in that visitor in your web-application with very little code:
PrincipalInfo.CurrentPrincipal = PrincipalInfo.CreatePrincipal(userName);
FormsAuthentication.SetAuthCookie(userName, true);
I would also give you an advice about using Session. Jeffery gives you a simple example of how to use the Session object. But don't go store multiple single values in Session; instead create a class like 'VisitorInformation' with all properties you will need. And then make a static manager that sets and gets that visitor information.
Visitor visitor = VisitorManager.CurrentVisitor;
string name = visitor.Name;
int age = visitor.Age;
private const visitorSessionKey = "visitorSessionkey";
public static Visitor CurrentVisitor
{
{
get { return (Visitor)Session[visitorSessionKey] ?? new Visitor(); }
}
}
This way you won't sprinkle your code with calls to session all over the place, with the increasing risk of spelling a key wrong or getting values out of sync.
You can read or write to Session almost anywhere in asp.net.
Examples:
using System.Web;
...
Session["UserAge"] = 28;
var userAge = (int)Session["UserAge"];
Session does have a timeout period, which may be configured in the web.config. As long as requests are being made to the browser, the session information should persist.
See MSDN for more info: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.aspx
There is a built-in object called Session that allows you to add and retrieve things from the Session. They are looked up automatically via a session cookie that is maintained by ASP.NET.