Please help! How to specify Dynamic Properties for ASP.NET Profiles - asp.net

My requirement is to create a framework that can consume standard ASP.NET Profile provider. The application contains two libraries, one for Profile access (saving and retrieving Profile using a ProfileBase class) – A framework class library, and the other one for defining properties for the Profile – Developer client class library which consumes the above framework class library.
The idea here is that the developers don’t need to know about the underlying profile implementation (in the framework class library), and all they have to do is to provide properties in a class so the profile can be set and get as expected.
My implementation is below.
(Please note that I have configured the authentication, connection strings and role providers successfully)
Web.config->
<profile inherits="MyCompany.FrameworkLib.ProfileSettingsService, MyCompany.FrameworkLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=12ac5ebb7ed144" >
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
</profile>
*Our design is not to specify profile properties in the web.config.
Implementation of MyCompany.FrameworkLib.ProfileSettingsService ->
public class ProfileSettingsService : ProfileBase, IProfileSettingsService
{
"**Note that if I un-comment the below code Profile works as expected. I do not want this property to be here, but somehow discover it dynamically based on the values being passed to this class. How can I do this?.**"
//[SettingsAllowAnonymous(false)]
//public string HideTransactionButton
//{
// get { return base["HideTransactionButton"] as string; }
// set { base["HideTransactionButton"] = value; }
//}
#region IProfileSettingsService Members
public T Get<T>(string key, T defaultValue)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
object profileValue = null;
try
{
profileValue = GetUserProfile().GetPropertyValue(key);
}
catch { }
if (profileValue is T)
{
return (T)profileValue;
}
return defaultValue;
}
public void Set<T>(string key, T value)
{
GetUserProfile().SetPropertyValue(key, value);
GetUserProfile().Save();
}
public ProfileSettingsService GetUserProfile(string username)
{
return Create(username) as ProfileSettingsService;
}
public ProfileSettingsService GetUserProfile()
{
var userName = HttpContext.User.Identity.Name;
if (userName != null)
{
return Create(userName) as ProfileSettingsService;
}
return null;
}
#endregion
}
implementation of MyCompany.ConsumeLib.ProfileContext ->
public class ProfileContext : IProfileContext
{
#region IProfileContext Members
[Dependency] //Note that I use Unity for DI
public IProfileSettingsService ProfileSettingsService { get; set; }
public string HideTransactionButton
{
get { return this.ProfileSettingsService.Get<string>("HideTransactionButton", "false"); }
set { this.ProfileSettingsService.Set("HideTransactionButton", value); }
}
#endregion
}
So the question is how to get the Profile working without having to uncomment
//[SettingsAllowAnonymous(false)]
//public string HideTransactionButton
//{
// get { return base["HideTransactionButton"] as string; }
// set { base["HideTransactionButton"] = value; }
//}
in MyCompany.FrameworkLib.ProfileSettingsService
I need to be able to discover properties dynamically within ProfileSettingsService without having to explicitly specifying properties. In this way, developer doesn’t need to worry about maintaining properties in two libraries - (one in the frameworkLib, and the other one is in the ConsumeLib.)
Any ideas greatly appreciated.

Visit this article, can help you:
http://msdn.microsoft.com/en-us/magazine/cc163724.aspx
also this page in MSDN
Defining ASP.NET Profile Properties

I have decided to take another approach to solver this problem. Unfortunately the way MS has designed the ProfileBase class there is no easy way to do this.
There are 2 possible solutions. I use below 'b'
a. Add profile properties to Web.config and use T4 templates dynamically generate them.
b. Have a look at the http://archive.msdn.microsoft.com/WebProfileBuilder. This will also dynamically generate profile properties at compile time.

Related

Confusion about generation of database and seeding

I am using entity framework code first
and also I have data seeding code
Now when I run my application my database gets generated but is not seeded with my dummy data.
I have to run entity framework once more to get all data populated.
Any idea why and how to fix that so i do not have to run my app 2x to get database and data?
thnx
my context definition file is:
public class Context : DbContext
{
public DbSet<Task> Tasks { get; set; }
public DbSet<Agency> Agency { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
And here is my seeding file
public class Configuration : DbMigrationsConfiguration<Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(Context context)
{
GenerateTasks(context);
context.SaveChanges();
}
private static void GenerateTasks(Context context)
{
if (context.Task.Any()) return;
context.Task.Add(new Task() { Name = "Received" });
}
}
And hook to create database is:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
var context = new Context();
context.Database.Initialize(true);
If your app is an ASP.NET one and is a seperate assembly from the data layer, then you can, instead of configuring the initialization like you did, configure it directly in the web.config. Maybe this is the reason for your problem.
So if it's an ASP.NET app, you can try the following:
(1)
Comment this out:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
var context = new Context();
context.Database.Initialize(true);
(2)
In web.config, insert this right at the end before the closing /configuration tag:
<entityFramework>
<contexts>
<context type="**fully-qualified name of your context class**,
**assembly name of your context**">
<databaseInitializer type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[**fully-qualified name of your context class**, **assembly name of your context**],
[**fully-qualified configuration type**, **assembly name of your context**, Version=1.0.0.0, Culture=neutral]], EntityFramework"/>
</context>
</contexts>
where fully-qualified configuration type is the class with your migrations-configuration (something like [...]Context.Migrations.Configuration)
I use this configuration-approach for myself in my projects and it works well!
That's true. call context.Tasks.Find(1) and then it will hit the database.
EF code-first uses this trick to postpone every thing. this way application's startup time seems much faster. (but actually it's not!)

Is there a step-by-step tutorial for configuring AppFabric Caching with .NET 4.0 Extensible Output Caching?

I want to use AppFabric (Velocity) as the Disk Caching Provider to work with ASP.NET 4.0's extensible output caching feature. But when I installed the AppFabric, I found it is incredibly hard to configure and I have no idea how can I make my ASP.NET app to work with it. So I was wondering is there a easy to understand tutorial for configuring both?
Or, is there any easier way other than AppFarbric to implement disk caching with ASP.NET?
I wrote some VB code for an AppFabricOutputCacheProvider in January - it's on my blog here. A C# (4.0) version would be:
using System.Web;
using Microsoft.ApplicationServer.Caching;
namespace AppFabricOutputCache
{
public class CacheProvider: System.Web.Caching.OutputCacheProvider, IDisposable
{
DataCache mCache;
const String OutputCacheName = "OutputCache";
public void New()
{
DataCacheFactory factory;
factory = new DataCacheFactory();
mCache = factory.GetCache(OutputCacheName);
}
public override Object Add(String key, Object entry, DateTime utcExpiry)
{
mCache.Add(key, entry, utcExpiry - DateTime.UtcNow);
return entry;
}
public override object Get(string key)
{
return mCache.Get(key);
}
public override void Remove(string key)
{
mCache.Remove(key);
}
public override void Set(string key, object entry, DateTime utcExpiry)
{
mCache.Put(key, entry, utcExpiry - DateTime.UtcNow);
}
public void IDisposable.Dispose()
{
mCache = null;
}
}
}
To use this in your application, you need this in your web.config.
<caching>
<outputCache>
<providers>
<add name="AppFabricOutputCacheProvider" type="AppFabricOutputCache.CacheProvider"/>
</providers>
</outputCache>
</caching>
Gunnar Peipman has a disk-based output cache provider on his blog here.

Is there a way to use a Dictionary-like collection as an Application Settings object?

I'd like to store a set of key/value pairs in the application settings of my ASP.NET web app, but I'm not finding a straightforward way to do that. For example, these two questions tell me that StringDictionary etc. won't serialize to XML and suggest that I'll have to roll my own implementation. But it seems like this should be easier to do; after all, web.config is XML and < applicationSettings> is essentially a collection of key/value pairs, so it feels like I'm missing something obvious. Given my specific case below, do I really have to roll my own serialization, or is there an easier workaround?
The web app in question is a basic contact form that sends email to different recipients based on the value of a parameter; e.g. http://www.examplesite.com/Contact.aspx?recipient=support would send email to SupportGroup#exampledomain.com.
The goal is to be able add or remove recipients (or change their addresses) by editing the web.config file so that I don't have to recompile and can easily maintain different configurations in test and production environments. For example:
// I can use something like this for the sender address
SmtpMsg.From = New MailAddress(My.Settings.EmailSender)
// And then just edit this part of web.config to use
// different addresses in different environments.
<setting name="EmailSender" serializeAs="String">
<value>webcontact#exampledomain.com</value>
</setting>
// I want something like this for the recipients
SmtpMsg.To.Add(My.Settings.Recipients("support"))
// and presumably some sort of equivalent xml in web.config
// maybe something like this???
<recipients>
<item name="support" serializeAs="String">
<value>SupportGroup#exampledomain.com</value>
</item>
<!-- add or remove item elements here -->
</recipients>
edit: replaced VB comments w/ C# comments because of the code-coloring
The simplist way would obviously be to just drop them in the app settings, but it wouldn't be very neat:
<appSettings>
<add key="EmailSupport" value="support#somedomain.com" />
<add key="EmailSales" value="sales#somedomain.com" />
</appSettings>
Then in your code you're just doing something like:
if (!string.IsNullOrEmpty(Request["recipient"])) {
string recipientEmail =
WebConfigurationManager.AppSettings["Email" + Request["recipient"]];
// Send your email to recipientEmail
}
If you want to be a bit neater, you can create a custom Configuration Section like this (C# I'm afraid, but the docs have VB as well):
namespace EmailSystem {
public class EmailRecipientsSection : ConfigurationSection {
[ConfigurationProperty("emailSender", IsRequired = true, IsKey = false)]
public string EmailSender {
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("emailRecipients", IsDefaultCollection = true)]
public EmailRecipientCollection EmailRecipients {
get {
var emailRecipientCollection =
(EmailRecipientCollection) base["emailRecipients"];
return emailRecipientCollection;
}
}
}
public class EmailRecipientCollection : ConfigurationElementCollection {
public EmailRecipientElement this[int index] {
get { return (EmailRecipientElement) BaseGet(index); }
set {
if (BaseGet(index) != null) {
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public new EmailRecipientElement this[string name] {
get { return (EmailRecipientElement) BaseGet(name); }
}
protected override ConfigurationElement CreateNewElement() {
return new EmailRecipientElement();
}
protected override object GetElementKey(ConfigurationElement element) {
return ((EmailRecipientElement) element).Name;
}
}
public class EmailRecipientElement : ConfigurationElement {
[ConfigurationProperty("name", IsRequired = true, IsKey = true)]
public string Name {
get { return (string) this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("emailAddress", IsRequired = true)]
public string EmailAddress {
get { return (string) this["emailAddress"]; }
set { this["emailAddress"] = value; }
}
}
}
Then in your web.config have something like this:
<configSections>
[...]
<section name="EmailSystem" type="EmailSystem, AssmeblyName" />
</configSections>
<EmailSystem emailSender="fromAddress#somedomain.com">
<emailRecipients>
<clear />
<add name="Support" emailAddress="support#somedomain.com" />
<add name="Sales" emailAddress="sales#somedomain.com" />
</emailRecipients>
</EmailSystem>
Then you can call into this:
emailRecipient = Request["recipient"];
var emailSystem = ConfigurationManager.GetSection("EmailSystem")
as EmailRecipientsSection;
string recipientEmail = emailSystem.EmailRecipients[emailRecipient].emailAddress;
// send email to recipientEmail.
You can do a couple things, though honestly I think this is easiest:
<appSettings>
<add key="testValues" value="someone#abc.com, someoneElse#abc.com, yetAnotherSomeone#abc.com" />
</appSettings>
Then you can get your object via:
String[] temp =
ConfigurationManager.AppSettings.GetValues("testValues").ToString().Split(',');
and then do a simple foreach statement to retrieve. You could even set this as a static object to be cached so it's a quicker retrieve. :)
Hope this helps,
JP
EDIT: An alternative scenario involves:
<appSettings file="test.config">
<!-- other settings to default to if test.config doesn't exist -->
</appSettings>
In this case, if you have a test.config file existing in your test environment, the AppSettings.GetValues() call will be made against that file. If the test.config file does not exist, the ConfigurationManager class will use the values within the appSettings node in your web.config file.

How to use Dependency Injection with ASP.NET Web Forms

I am trying to work out a way to use dependency injection with ASP.NET Web Forms controls.
I have got lots of controls that create repositories directly, and use those to access and bind to data etc.
I am looking for a pattern where I can pass repositories to the controls externally (IoC), so my controls remain unaware of how repositories are constructed and where they come from etc.
I would prefer not to have a dependency on the IoC container from my controls, therefore I just want to be able to construct the controls with constructor or property injection.
(And just to complicate things, these controls are being constructed and placed on the page by a CMS at runtime!)
Any thoughts?
UPDATE 2019:
With the introduction of Web Forms 4.7.2, there is now better support for DI. This invalidates the below. See: Wiring up Simple Injector in WebForms in .NET 4.7.2
You can use automatic constructor injection by replacing the default PageHandlerFactory with a custom one. This way you can use an overloaded constructor to load the dependencies. Your page might look like this:
public partial class HomePage : System.Web.UI.Page
{
private readonly IDependency dependency;
public HomePage(IDependency dependency)
{
this.dependency = dependency;
}
// Do note this protected ctor. You need it for this to work.
protected HomePage () { }
}
Configuring that custom PageHandlerFactory can be done in the web.config as follows:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.aspx"
type="YourApp.CustomPageHandlerFactory, YourApp"/>
</httpHandlers>
</system.web>
</configuration>
Your CustomPageHandlerFactory can look like this:
public class CustomPageHandlerFactory : PageHandlerFactory
{
private static object GetInstance(Type type)
{
// TODO: Get instance using your favorite DI library.
// for instance using the Common Service Locator:
return Microsoft.Practices.ServiceLocation
.ServiceLocator.Current.GetInstance(type);
}
public override IHttpHandler GetHandler(HttpContext cxt,
string type, string vPath, string path)
{
var page = base.GetHandler(cxt, type, vPath, path);
if (page != null)
{
// Magic happens here ;-)
InjectDependencies(page);
}
return page;
}
private static void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
var ctor = GetInjectableCtor(pageType);
if (ctor != null)
{
object[] arguments = (
from parameter in ctor.GetParameters()
select GetInstance(parameter.ParameterType)
.ToArray();
ctor.Invoke(page, arguments);
}
}
private static ConstructorInfo GetInjectableCtor(
Type type)
{
var overloadedPublicConstructors = (
from constructor in type.GetConstructors()
where constructor.GetParameters().Length > 0
select constructor).ToArray();
if (overloadedPublicConstructors.Length == 0)
{
return null;
}
if (overloadedPublicConstructors.Length == 1)
{
return overloadedPublicConstructors[0];
}
throw new Exception(string.Format(
"The type {0} has multiple public " +
"ctors and can't be initialized.", type));
}
}
Downside is that this only works when running your side in Full Trust. You can read more about it here. But do note that developing ASP.NET applications in partial trust seems a lost cause.
Starting from .NET 4.7.2 (what's new), it is now easy for developers to use Dependency Injection in WebForms applications. With the UnityAdapter, you can add it to your existing WebForms application in 4 simple steps. See this blog.
Autofac supports fairly unobtrusive dependency injection in ASP.NET WebForms. My understanding is it just hooks into the ASP.NET page lifecycle using an http module and does property injection. The only catch is that for controls I don't think this happens until after the Init event.
The best way is to have a base class for the controls like:
public class PartialView : UserControl
{
protected override void OnInit(System.EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnInit(e);
}
}
That will inject any control that inherits from that base class (uses structuremap). Combining that with a property based config, you will be able to have controls like:
public partial class AdminHeader : PartialView
{
IMyRepository Repository{get;set;}
}
Update 1: If you can't have the controls inherit, perhaps the CMS has a hook right after creating the controls, in there you can call the BuildUp. Also if the CMS allows you to hook something to fetch the instance you could use constructor based injection, but I prefer BuildUp on this specific scenario as asp.net doesn't have a hook for this.
You could also create some singleton instances in the Application_Start global.asax event and have them available as public static readonly properties.
This is a solution I recently used to avoid hooking into the pipeline (I find that confuses everyone that looks at my code in the future, but yes, I see its benefits as well):
public static class TemplateControlExtensions
{
static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager();
private static WIIIPDataContext GetDataContext(this TemplateControl templateControl)
{
var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext");
if (dataContext == null)
{
dataContext = new WIIIPDataContext();
perRequestObjectManager.SetValue("DataContext", dataContext);
}
return dataContext;
}
public static IMailer GetMailer(this TemplateControl templateControl)
{
return (IMailer)IoC.Container.Resolve(typeof(IMailer));
}
public static T Query<T>(this TemplateControl templateControl, Query<T> query)
{
query.DataContext = GetDataContext(templateControl);
return query.GetQuery();
}
public static void ExecuteCommand(this TemplateControl templateControl, Command command)
{
command.DataContext = GetDataContext(templateControl);
command.Execute();
}
private class PerRequestObjectManager
{
public object GetValue(string key)
{
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public void SetValue(string key, object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
}
This shows how you can create your own life time manager pretty easily as well as hook into an IoC container if you so desire. Oh, and I am also using a query/command structure which is sort of unrelated, but more on the reasoning behind that can be found here:
Limit your abstractions: Refactoring toward reduced abstractions

How do I define custom web.config sections with potential child elements and attributes for the properties?

The web applications I develop often require co-dependent configuration settings and there are also settings that have to change as we move between each of our environments.
All our settings are currently simple key-value pairs but it would be useful to create custom config sections so that it is obvious when two values need to change together or when the settings need to change for an environment.
What's the best way to create custom config sections and are there any special considerations to make when retrieving the values?
Using attributes, child config sections and constraints
There is also the possibility to use attributes which automatically takes care of the plumbing, as well as providing the ability to easily add constraints.
I here present an example from code I use myself in one of my sites. With a constraint I dictate the maximum amount of disk space any one user is allowed to use.
MailCenterConfiguration.cs:
namespace Ani {
public sealed class MailCenterConfiguration : ConfigurationSection
{
[ConfigurationProperty("userDiskSpace", IsRequired = true)]
[IntegerValidator(MinValue = 0, MaxValue = 1000000)]
public int UserDiskSpace
{
get { return (int)base["userDiskSpace"]; }
set { base["userDiskSpace"] = value; }
}
}
}
This is set up in web.config like so
<configSections>
<!-- Mailcenter configuration file -->
<section name="mailCenter" type="Ani.MailCenterConfiguration" requirePermission="false"/>
</configSections>
...
<mailCenter userDiskSpace="25000">
<mail
host="my.hostname.com"
port="366" />
</mailCenter>
Child elements
The child xml element mail is created in the same .cs file as the one above. Here I've added constraints on the port. If the port is assigned a value not in this range the runtime will complain when the config is loaded.
MailCenterConfiguration.cs:
public sealed class MailCenterConfiguration : ConfigurationSection
{
[ConfigurationProperty("mail", IsRequired=true)]
public MailElement Mail
{
get { return (MailElement)base["mail"]; }
set { base["mail"] = value; }
}
public class MailElement : ConfigurationElement
{
[ConfigurationProperty("host", IsRequired = true)]
public string Host
{
get { return (string)base["host"]; }
set { base["host"] = value; }
}
[ConfigurationProperty("port", IsRequired = true)]
[IntegerValidator(MinValue = 0, MaxValue = 65535)]
public int Port
{
get { return (int)base["port"]; }
set { base["port"] = value; }
}
Use
To then use it practically in code, all you have to do is instantiate the MailCenterConfigurationObject, this will automatically read the relevant sections from web.config.
MailCenterConfiguration.cs
private static MailCenterConfiguration instance = null;
public static MailCenterConfiguration Instance
{
get
{
if (instance == null)
{
instance = (MailCenterConfiguration)WebConfigurationManager.GetSection("mailCenter");
}
return instance;
}
}
AnotherFile.cs
public void SendMail()
{
MailCenterConfiguration conf = MailCenterConfiguration.Instance;
SmtpClient smtpClient = new SmtpClient(conf.Mail.Host, conf.Mail.Port);
}
Check for validity
I previously mentioned that the runtime will complain when the configuration is loaded and some data does not comply to the rules you have set up (e.g. in MailCenterConfiguration.cs). I tend to want to know these things as soon as possible when my site fires up. One way to solve this is load the configuration in _Global.asax.cx.Application_Start_ , if the configuration is invalid you will be notified of this with the means of an exception. Your site won't start and instead you will be presented detailed exception information in the Yellow screen of death.
Global.asax.cs
protected void Application_ Start(object sender, EventArgs e)
{
MailCenterConfiguration.Instance;
}
Quick'n Dirty:
First create your ConfigurationSection and ConfigurationElement classes:
public class MyStuffSection : ConfigurationSection
{
ConfigurationProperty _MyStuffElement;
public MyStuffSection()
{
_MyStuffElement = new ConfigurationProperty("MyStuff", typeof(MyStuffElement), null);
this.Properties.Add(_MyStuffElement);
}
public MyStuffElement MyStuff
{
get
{
return this[_MyStuffElement] as MyStuffElement;
}
}
}
public class MyStuffElement : ConfigurationElement
{
ConfigurationProperty _SomeStuff;
public MyStuffElement()
{
_SomeStuff = new ConfigurationProperty("SomeStuff", typeof(string), "<UNDEFINED>");
this.Properties.Add(_SomeStuff);
}
public string SomeStuff
{
get
{
return (String)this[_SomeStuff];
}
}
}
Then let the framework know how to handle your configuration classes in web.config:
<configuration>
<configSections>
<section name="MyStuffSection" type="MyWeb.Configuration.MyStuffSection" />
</configSections>
...
And actually add your own section below:
<MyStuffSection>
<MyStuff SomeStuff="Hey There!" />
</MyStuffSection>
Then you can use it in your code thus:
MyWeb.Configuration.MyStuffSection configSection = ConfigurationManager.GetSection("MyStuffSection") as MyWeb.Configuration.MyStuffSection;
if (configSection != null && configSection.MyStuff != null)
{
Response.Write(configSection.MyStuff.SomeStuff);
}
The custom configuration are quite handy thing and often applications end up with a demand for an extendable solution.
For .NET 1.1 please refer the article https://web.archive.org/web/20211027113329/http://aspnet.4guysfromrolla.com/articles/020707-1.aspx
Note: The above solution works for .NET 2.0 as well.
For .NET 2.0 specific solution, please refer the article https://web.archive.org/web/20210802144254/https://aspnet.4guysfromrolla.com/articles/032807-1.aspx
You can accomplish this with Section Handlers. There is a basic overview of how to write one at http://www.codeproject.com/KB/aspnet/ConfigSections.aspx however it refers to app.config which would be pretty much the same as writing one for use in web.config. This will allow you to essentially have your own XML tree in the config file and do some more advanced configuration.
The most simple method, which I found, is using appSettings section.
Add to Web.config the following:
<appSettings>
<add key="MyProp" value="MyVal"/>
</appSettings>
Access from your code
NameValueCollection appSettings = ConfigurationManager.AppSettings;
string myPropVal = appSettings["MyProp"];

Resources