5

I have a simple application that sends out status emails to some of our internal users.

I use a simple application configuration file (App.config) to store email address and name information, about the intended users. Since the appSettings section only seem to support simple key/value pairs, it currently looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="toName" value="Recipient Name" />
    <add key="toAddr" value="some@email.com" />
    <add key="toName2" value="Another Recipient Name" />
    <add key="toAddr2" value="another@email.com" />
    <add key="ccName" value="An Archive"/>
    <add key="ccAddr" value="copies@email.com"/>
    <add key="ccName2" value="Another Archive"/>
    <add key="ccAddr2" value="morecopies@email.com"/>
  </appSettings>
</configuration>

And then I add each recipient individually in the code.

Currently, this means that every time I add or remove recipients, I also need to rewrite the code to handle the new recipients and rebuild and re-deploy the application

I would like to be able to store custom configuration entries, like this maybe:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <recipients>
    <recipient recType="to" recAddr="some@email.com" recName="Recipient Name" />
    <recipient recType="to" recAddr="another@email.com" recName="Another Recipient Name" />
    <recipient recType="cc" recAddr="copies@email.com" recName="An Archive"/>
    <recipient recType="cc" recAddr="morecopies@email.com" recName="Another Archive"/>
  </recipients>
</configuration>

So I can loop through them:

MailMessage message = new MailMessage();
foreach(recipient rec in recipients)
{
  MailAddress mailAddress = new MailAddress(recipient["recAddr"],recipient["recName"]);
  if(recipient["recType"] == "cc")
    message.CC.Add(mailAddress);
  else
    message.To.Add(mailAddress);
}

How to accomplish this?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • 2
    You could create a custom config section, as outlined at http://stackoverflow.com/questions/758986/custom-app-config-config-section-handler – stuartd Dec 18 '12 at 11:43
  • @StuartDunkeld very nice, exactly something like this I was looking for, even though it seems a little exagerated for this (seamingly) simple problem – Mathias R. Jessen Dec 18 '12 at 12:23
  • @MathiasR.Jessen it seems exagerated because your using the configuration file for something it wasn't designed to do. The time you spend trying to hack the configuration file could be spent towards just creating your own simple xml file designed exactly how you want it to be. – Security Hound Dec 18 '12 at 12:38
  • If you return new MailAddress(string) from the config value you can have the name and address in one value. – ccook Dec 18 '12 at 13:16

6 Answers6

3

You should write a custom config section for your recipients and then include this section. With the custom section you will be also able to store the recipients config info outside the main config file and include it using the configSource attribute.

For a start you can look here: http://haacked.com/archive/2007/03/11/custom-configuration-sections-in-3-easy-steps.aspx

In short, you should:

  1. Implement your custom config section by inheriting from ConfigurationElement (for one element) and ConfigurationElementCollection (for collections, you need collection in your case and each recipient will be element of connection). Sample implementation is in answer here: how to have custom attribute in ConfigurationElementCollection?

  2. Define config section in main config

  3. Add your custom configuration and it could be included as separate config file

TylerH
  • 20,799
  • 66
  • 75
  • 101
Regfor
  • 8,515
  • 1
  • 38
  • 51
  • Two more guide links: http://msdn.microsoft.com/en-us/library/2tw134k3.aspx or here with configSource http://joelabrahamsson.com/entry/creating-a-custom-configuration-section-in-net – Regfor Dec 18 '12 at 12:07
  • Already looked at the 3-easy-steps article, not much help, I need a collection of elements (multiple recipients), the article enly deals with 1 at a time. That last link from joel abrahamsson is very interesting though – Mathias R. Jessen Dec 18 '12 at 12:27
  • No, that's only for start to understand idea of custom config sections. Have you checked step 2 link http://stackoverflow.com/questions/8829414/how-to-have-custom-attribute-in-configurationelementcollection ? – Regfor Dec 18 '12 at 12:29
1

Create you custom section in the web.config file. You can find some examples how to do that here http://haacked.com/archive/2007/03/11/custom-configuration-sections-in-3-easy-steps.aspx or you can search for other resources.

Than you can map the entities from the section to some kind of POCO that you'll create for representing the Email receiver.

This way you can operate with a collection of email receivers that will easy up your work.

And don't forget to create a service layer for sending the emails.

So here are the steps you have to do:

  1. Create a custom config section.
  2. Create a Poco representation of the email receiver
  3. Map the entities from the custom config to a poco collection
  4. Create a service or simple helper for sending emails

It is a good practice to separate the domain/application specific logic to separate files, so please take a look at this question as well: Moving a custom configuration group to a separate file

TylerH
  • 20,799
  • 66
  • 75
  • 101
CoffeeCode
  • 4,296
  • 9
  • 40
  • 55
  • Not ASP, but i guess that's irrelevant. However, I looked at the 3-easy-steps solution and the custom configuration element in the example is unique, ie. only 1 BlogSettings element. I need to store a collection of elements – Mathias R. Jessen Dec 18 '12 at 12:17
0

You can create custom config sections, look here: http://msdn.microsoft.com/en-us/library/2tw134k3(v=vs.100).aspx

Intended for ASP.NET, also possible for WPF and WinForms I think.

Davio
  • 4,609
  • 2
  • 31
  • 58
0

You could store the Name/Address values in a section per mail addressing.

<configuration>
  <configSections>
    <section 
      name="MailAddressing" 
      type="System.Configuration.NameValueFileSectionHandler,System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>

  <MailAddressing>
     <add key="To" value="&quot;An Example&quot; <someone@example.com>;&quot;Second Example&quot; <another@example.com>" />
     <add key="CC" value="someone@example.com" />
     <add key="BCC" value="third@example.com" />
     <add key="From" value="sender@example.com" />
  </MailAddressing>
</configuration>

Then access the addresse(s) by

    NameValueCollection section =
        (NameValueCollection)ConfigurationManager.GetSection("MailAddressing");

Perhaps the simplest solution for serialization is to use the string converters in the MailAddress class to handle the values of the settings.

// Test data
var addressList = new[]
    {
        new MailAddress("someone@example.com", "An Example"),
        new MailAddress("another@example.com", "Second Example")
    };

// To String for saving in config
string strValue = addressList.Select(i => i.ToString())
            .Aggregate((i, j) => i + ';' + j);

// From String for reading from config
MailAddress[] addressList2 = strValue.Split(';').Select(i => 
            new MailAddress(i)).ToArray();

Now you can have one config setting per To/CC/BCC value grouped by mail addressing. It will work with a single address or many, with or without the display name.

ccook
  • 5,869
  • 6
  • 56
  • 81
  • Yeah, I tried to fiddle around with the NameValueCollection type like in your example, but I still face the key/value limitation, and I have multiple properties per entry (name, address, to/cc) – Mathias R. Jessen Dec 18 '12 at 12:21
  • I think you will like what I added then :) – ccook Dec 18 '12 at 12:35
0

While I agree that a custom configuration section is a valid approach, it is possible to put multiple addresses including display names in a single appSetting. E.g.:

<add key="To" 
  value='"Recipient Name" &lt;some@email.com>, "Another Recipient Name" &lt;another@email.com>'/>

...
string to = ConfigurationManager.AppSettings["To"];
MailMessage m = new MailMessage();
m.To.Add(to);
Joe
  • 122,218
  • 32
  • 205
  • 338
-1

Migrating OP's solution from the question to an answer:

Using the linked example in Regfor's answer, I was able to build a custom configuration section with a collection of custom ConfigurationElements looking like this:

public class RecipientElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name
    {
        get
        {
            return (string)base["name"];
        }
    }

    [ConfigurationProperty("mailAddr", IsRequired = true)]
    public string Address
    {
        get
        {
            return (string)base["mailAddr"];
        }
    }

    [ConfigurationProperty("isCC")]
    public bool IsCC
    {
        get
        {
            return (bool)base["isCC"];
        }
    }
}

with the final config section:

<recipientSection>
  <recipients>
    <recipient name="Primary recipient" mailAddr="usermailbox@email.com" isCC="false" />
    <recipient name="Archive" mailAddr="copies@email.com" isCC="true" />
  </recipients>
</recipientSection>

Looping through the recipients collection lets me add as many recipients as the SmtpClient will let me send to.

TylerH
  • 20,799
  • 66
  • 75
  • 101