C#
Interface Segregation Principle in C# – SOLID Design Principles – Part 4
Overview
In our introduction to the SOLID Design Principles, we mentioned the Interface Segregation Principle as one of the five principles specified. In this post we are going to dive into this design principle with a very simple example in C#.
In the last post on the Liskov Substitution Principle, we utilized the Interface Segregation Principle to refactor our code. The code that we write in this post will be very simple as well and will take that code and introduce another segregation of our interfaces. I generally do not write posts that are “recaps” of previous posts, but our implementation from the last post certainly warrants a “recap” in this one.
The Interface Segregation Principle states that clients (classes) should not be forced to implement interfaces that they do not use. Well this certainly sounds reasonable, doesn’t it?
So if we start with our original design from the Liskov Substitution Principle post, here is our original design and code:
public class Engine { public IgnitionResult Start(IStarter starter) { return starter.Start(); } } public interface IStarter { string Brand { get; set; } string Model { get; set; } IgnitionResult Start(); } public class ElectricStarter : IStarter { public string Brand { get; set; } public string Model { get; set; } public IgnitionResult Start() { //code here to initiate the electric starter return IgnitionResult.Success; } } public class PneumaticStarter : IStarter { public string Brand { get; set; } public string Model { get; set; } public IgnitionResult Start() { //code here to initiate the pneumatic starter return IgnitionResult.Success; } } public class HydraulicStarter : IStarter { public string Brand { get; set; } public string Model { get; set; } public IgnitionResult Start() { //code here to initiate the hydraulic starter return IgnitionResult.Success; } } public enum IgnitionResult { Success, Failure }
In our last post, instead of a single Starter class, we created a distinct type for each type of Starter – electric, pneumatic, and hydraulic. Then we realized that in actual implementation, each type of starter had a different requirement for the object it used to actually work. The ElectricStarter utilized a Battery, the PneumaticStarter utilized an AirCompressor, and the HydraulicStarter utilized a HydraulicPump. When we actually wrote the code needed to make these work, we realized that our single interface just did NOT make sense. See the code below.
public interface IStarter { string Brand { get; set; } string Model { get; set; } Battery Battery { get; set; } AirCompressor Compressor { get; set; } HydraulicPump Pump { get; set; } IgnitionResult Start(); } public class ElectricStarter : IStarter { public string Brand { get; set; } public string Model { get; set; } public Battery Battery { get; set; } public AirCompressor Compressor { get { throw new NotImplementedException("An Electric Starter does not use an AirCompressor."); } set { throw new NotImplementedException("An Electric Starter does not use an AirCompressor."); } } public HydraulicPump Pump { get { throw new NotImplementedException("An Electric Starter does not use a HydraulicPump."); } set { throw new NotImplementedException("An Electric Starter does not use an HydraulicPump."); } } public IgnitionResult Start() { //code here to initiate the electric starter return IgnitionResult.Success; } } public class PneumaticStarter : IStarter { public string Brand { get; set; } public string Model { get; set; } public Battery Battery { get { throw new NotImplementedException("An PneumaticStarter does not use a Battery."); } set { throw new NotImplementedException("An PneumaticStarter does not use an Battery."); } } public AirCompressor Compressor { get; set; } public HydraulicPump Pump { get { throw new NotImplementedException("An PneumaticStarter does not use a HydraulicPump."); } set { throw new NotImplementedException("An PneumaticStarter does not use an HydraulicPump."); } } public IgnitionResult Start() { //code here to initiate the pneumatic starter return IgnitionResult.Success; } } public class HydraulicStarter : IStarter { public string Brand { get; set; } public string Model { get; set; } public Battery Battery { get { throw new NotImplementedException("A HydraulicStarter does not use a Battery."); } set { throw new NotImplementedException("A HydraulicStarter does not use an Battery."); } } public AirCompressor Compressor { get { throw new NotImplementedException("A HydraulicStarter does not use an AirCompressor."); } set { throw new NotImplementedException("A HydraulicStarter does not use an AirCompressor."); } } public HydraulicPump Pump { get; set; } public IgnitionResult Start() { //code here to initiate the hydraulic starter return IgnitionResult.Success; } } public class Battery { } public class AirCompressor { } public class HydraulicPump { } public enum IgnitionResult { Success, Failure }
Upon reviewing this code more, it became painfully apparent that it was quite bad and required some refactoring. So, per the Interface Segregation Principle, we broke out the interfaces that we actually needed and went from one interface to four interfaces. As a result, each Starter class implemented its own interface that interface had only what was needed for that type. Nice! Our final code from the last post is below:
public interface IStarter { string Brand { get; set; } string Model { get; set; } IgnitionResult Start(); } public interface IElectricStarter : IStarter { Battery Battery { get; set; } } public interface IPneumaticStarter : IStarter { AirCompressor Compressor { get; set; } } public interface IHydraulicStarter : IStarter { HydraulicPump Pump { get; set; } }
We picked the interface segregation is our starting point because it sets the stage for the changes that need to be made to the concrete classes.
So then we rewrote our Starter classes to implement their respective interfaces:
public class ElectricStarter : IElectricStarter { public string Brand { get; set; } public string Model { get; set; } public Battery Battery { get; set; } public IgnitionResult Start() { //code here to initiate the electric starter return IgnitionResult.Success; } } public class PneumaticStarter : IPneumaticStarter { public string Brand { get; set; } public string Model { get; set; } public AirCompressor Compressor { get; set; } public IgnitionResult Start() { //code here to initiate the pneumatic starter return IgnitionResult.Success; } } public class HydraulicStarter : IHydraulicStarter { public string Brand { get; set; } public string Model { get; set; } public HydraulicPump Pump { get; set; } public IgnitionResult Start() { //code here to initiate the hydraulic starter return IgnitionResult.Success; } }
And in doing that, we were introduced to the Interface Segregation Principle and used it to help us adhere to another principle. The overlap among the SOLID design principles is so nice!
In the next post, we are going to dive more deeply into the Dependency Inversion Principle. See the links below for all posts related to the SOLID Design Principles.
The SOLID Design Principles
The Single Responsibility Principle (SRP)
The Open-Closed Principle (OCP)
The Liskov Substitution Principle (LSP)
The Interface Segregation Principle (ISP)
The Dependency Inversion Principle (DIP)
SOLID Design Principles
In software development, principles differ from patterns in the sense that where patterns represent complete, identifiable, repeatable solutions to common problems, principles are objective, factual statements that can be made about code and the manner in which it is constructed and the overall design of an implementation. In other words, patterns refer to code scenarios while principles refer to qualities of code and these qualities are useful in identifying the value of the code.
In this post, we are going to be introduced to Bob Martin’s SOLID design principles. These principles have been around for a long time, and it is immeasurably important for every object-oriented developer to understand them and use them in making day-to-day design decisions. It is not that uncommon to see developers dive into development tasks by writing code first and considering architecture and design second. A good developer though inverts this scenario and considers a sound design before writing the first line of code! This prevents undue code maintenance pain later and allows for the development of better applications through and through.
Consideration of design principles is extremely important throughout a development effort, and failure to make the proper considerations can have a devastating effect on the development of the application and the application’s usefulness, performance, and maintainability.
What are the SOLID Design Principles?
In this section, we are going to outline and briefly discuss each of the five design principles. In subsequent posts we will dive into each principle in more detail. The five SOLID design principles are listed below:
- The Single Responsibility Principle (SRP) – this principle states that there should never be more than one reason to change a class. This means also that a given class should exist for one and only one purpose.
- The Open-Closed Principle – this principle states that modules should be open for extension but closed for modification. This seems a bit unclear at first until you realize that you can change an object’s behavior by either using abstractions, implementing common interfaces, and from inheritance from common base classes or extending abstract base classes.
- The Liskov Substitution Principle – this principle, introduced by Barbara Liskov, simply-stated means that derivative classes have to be substitutable for their base classes. If we think about this for a minute, we can see that this also means that any class that implements a specific interface can be replaced by any other class that implements that interface. In other words, it can be substituted for the original class. Furthermore, a derived class must honor the ‘contract’ set by its super class.
- The Interface Segregation Principle (ISP) – this principle states that objects should not be forced to implement interfaces that they do not use. Though this may sound obvious based on the wording, in practice it really means that interfaces should be finely-grained and not specific to the classes for which their implementation is intended.
- The Dependency Inversion Principle (DIP) – this principle states that higher-level classes should not depend upon lower-level classes but that both should depend on abstractions. Furthermore, these abstractions should not depend on concrete details but rather on abstractions as well.
Though each principle can be discussed individually, we should understand that no single principle should exist or be applied by itself. They should all be considered as part of the design process. In the following posts, we will dive into each principle in detail and take a look at some simplified code examples that illustrate each one.
The Single Responsibility Principle (SRP)
The Open-Closed Principle (OCP)
The Liskov Substitution Principle (LSP)
The Interface Segregation Principle (ISP)
The Dependency Inversion Principle (DIP)
SOAP Serialization with C# and .NET
In the previous post, we discussed binary serialization with C# and .NET. In this short article we are going to dive into some specific examples of use of the SoapFormatter class to SOAP serialize objects to XML and deserialize from XML back into objects.
We will discuss SOAP a little later in this article 🙂
SOAP Serialization with the SoapFormatter class
To get started, let’s create a simple class, add some attributes to help us control how properties are serialized, then we’ll serialize and deserialize the object.
To get started, let’s create a C# Console Application that we will call SoapSerializationSample.

Next, we will add a class to the project. We will call this class Sample.
Let’s now add two properties – Name and Value as shown below then decorate the class with the Serializable attribute.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SoapSerializationSample { [Serializable] public class Sample { public string Name { get; set; } public Int32 Value { get; set; } } }
As you can see, this class is very simple and is a great starting point for examining SOAP serialization.
The Serializable attribute signals the .NET runtime that instances of this class can be serialized. To use serialization, this attribute must be in place at the class level.
Before we write the serialization code, let’s first add the references shown in the illustration below:

Next, let’s add the following using statements to our Program class:
using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Soap;
In our Program.cs class’s Main() method, we are going to write some simple code that uses the SoapFormatter to serialize an instance of our Sample object into a Soap XML stream.
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Soap; namespace SoapSerializationSample { class Program { static void Main(string[] args) { //create an instance of our Sample class //which we will serialize below Sample sample = new Sample(); sample.Name = "John Nelson"; sample.Value = 44; //create a FileStream to accept the output FileStream fileStream = new FileStream(@"c:\temp\MySoapFile.dat", FileMode.Create); //create a SoapFormatter to serialize the object SoapFormatter formatter = new SoapFormatter(); //serialize the object to the .dat file formatter.Serialize(fileStream, sample); } } }
When we press F5 and run the application, the SoapFormatter serializes the Sample object to our MySoapFile.dat file. If we open this file in Visual Studio, we see something that looks like this:

So, some of you are probably looking at this output and screaming “this is NOT well-formed XML!” and you are right! This file does not contain an xml declaration does not contain opening and closing xml tags. Though an in-depth discussion of SOAP is well beyond the purpose and scope of this article, it will be beneficial to briefly discuss the parts of a SOAP message.
What is SOAP?
Soap is an acronym for Simple Object Access Protocol. It is basically an XML-based protocol for transmitting data between computers and is designed for for data communication via the internet and provides the transport mechanism for web services. SOAP is language and platform independent.
SOAP Message Structure
SOAP messages are encoded as XML documents. These documents begin with a mandatory <Envelope> element and may or may not contain a <Header> element. The <Body> element is mandatory for all SOAP messages, so we would expect to always see that. A <Fault> element may or may not be contained within the <Body> element and is used to provide information about exceptions or errors that may have occurred while the message was processed.
<Envelope> Element
The <Envelope> element is the start and end of the message. The entire SOAP message is contained within the <Envelope> element. Every <Envelope> element must contain exactly one <Body> element – no exceptions! The <Envelope> will change if the version of the message changes.
<Header> Element
<Header> elements are optional within the <Envelope> element and there can be multiple <Header> elements in a SOAP message.
<Body> Element
The <Body> element contains the actual data being transmitted in the SOAP message. You can think of it as the “guts” of the message or the payload of the message.
<Fault> Element
If you have ever worked with WCF, you are familiar with the FaultExceptions. When an error occurs during processing by a web service, information pertaining to that error can be encapsulated within a <Fault> block. If a <Fault> element is included in a SOAP message, there can only be one.
Learning More About SOAP
If you are interested in learning more about the SOAP standard, try this link.
Deserializing with the SoapFormatter
Deserializing with the SoapFormatter is as simple as serializing. See the code below:
//create a FileStream to open the .dat file FileStream fileStream = new FileStream(@"c:\temp\MySoapFile.dat", FileMode.Open); //create a SoapFormatter to deserialize the object SoapFormatter formatter = new SoapFormatter(); //serialize the object to the .dat file Sample deserializedSample = (Sample)formatter.Deserialize(fileStream); //show the object properties Console.WriteLine("The deserialized object:"); Console.WriteLine(String.Format("Name: {0}", deserializedSample.Name)); Console.WriteLine(String.Format("Value: {0}", deserializedSample.Value.ToString())); Console.Read();
When we press F5 and run our application, we see the values of our object displayed in the console window:

XML Serialization with C# and .NET
In the previous post, we discussed the basics of serialization in .NET. In this short article we are going to dive into some specific examples of use of the XmlSerializer to serialize objects to XML and deserialize from XML back into objects 🙂
XML Serialization with the System.Xml.Serialization.XmlSerializer class
To get started, let’s create a simple class, add some attributes to help us control how properties are serialized, then we’ll serialize and deserialize the object.
To get started, let’s create a C# Console Application that we will call SerializationSample.

Next, we will add a class to the project. We will call this class Sample.
Let’s now add two properties – Name and Value as shown below.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SerializationSample { public class Sample { public string Name { get; set; } public Int32 Value { get; set; } } }
As you can see, this class is very simple and is a great starting point for examining Xml serialization.
In our Program.cs class’s Main() method, we are going to write some simple code that uses the XmlSerializer to serialize an instance of our Sample object into an XML stream.
static void Main(string[] args) { //let's first instantiate our Sample object //and set its two properties Sample sample = new Sample(); sample.Name = "John Nelson"; sample.Value = 44; //next, create an instance of an XmlSerializer //we will use the Type constructor for this example XmlSerializer serializer = new XmlSerializer(typeof(Sample)); //serialize the object to the Console serializer.Serialize(Console.Out, sample); Console.ReadLine(); }
When we press F5 and run the console application, our console window displays the resulting XML. See the following illustration.

So when we look at the resulting XML, we see the standard xml declaration followed by the opening tag for our Sample object, an XML namespace (xmlns) attribute, and the values of the two properties in our object. Pretty simple, right?
XML Namespaces
Though my intent in this article is to discuss XML serialization and not XML and its accompanying concepts, we do need to briefly talk about the concept of XML namespaces. XML namespaces provide a couple of things in our generated XML. First, they remove any ambiguity between two or more elements that happen to have the same name. Second, they are useful for grouping elements that relate and adhere to a common “idea” together.
A namespace is identified and conveyed via a uri. So what does that mean? Well, I could define a namespace that I could use for all of my XML “objects” based on http://johnlnelson.com. I could for the sake of this example specify the namespace to be https://johnlnelson.com/namespaces/sample. It is a safe assumption that anyone who produces XML for the purpose of transmitting it via HTTP would do this via a domain, such as johnlnelson.com.
Armed with this little bit of knowledge, let’s modify our code to specify our own namespace and prefix.
static void Main(string[] args) { //let's first instantiate our Sample object //and set its two properties Sample sample = new Sample(); sample.Name = "John Nelson"; sample.Value = 44; //next, create an instance of an XmlSerializer //we will use the Type constructor for this example XmlSerializer serializer = new XmlSerializer(typeof(Sample)); //specify our namespace XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add("jln", "https://johnlnelson.com/namespaces/sample"); //serialize the object to the Console //with our specified namespace serializer.Serialize(Console.Out, sample, namespaces); Console.ReadLine(); }
Pay attention to lines 14-15 and the new overload of the Serialize() method in line 19.
We created an instance of an XmlSerializerNamespaces object and added one prefix/namespace combination. The prefix I chose is “jln” and my namespace is https://johnlnelson.com/namespaces/sample.
When we press F5 to launch the application and view our XML output, we see our own namespace in the XML.

Pretty simple? Sure it is. Now let’s take a look at the encoding attribute in our xml document tag. What if we don’t want to use the default IBM437 encoding? We can do that too, but the code is slightly more involved (but not too bad).
Let’s modify our serialization code to look like this:
//let's first instantiate our Sample object //and set its two properties Sample sample = new Sample(); sample.Name = "John Nelson"; sample.Value = 44; //next, create an instance of an XmlSerializer //we will use the Type constructor for this example XmlSerializer serializer = new XmlSerializer(typeof(Sample)); //specify our namespace XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add("jln", "https://johnlnelson.com/namespaces/sample"); //create an XmlWriterSettings object to specify the //encoding and the indentation XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = new UTF8Encoding(); settings.Indent = true; //create an XmlWriter that utilizes a StringWriter to //build the output, then write that to the Console window using (StringWriter stringWriter = new StringWriter()) { using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings)) { serializer.Serialize(xmlWriter, sample, namespaces); Console.Write(stringWriter); } } Console.ReadLine();
You will see quite a few changes from lines 17 to 30. We will discuss the changes in a bit, but first let’s press F5 and run our code! The console window will now look like this:

Okay, so what’s going on here? First, in order to take the level of control over our encoding, we had to initiate the help of a StringWriter and an XmlWriter. We created an XmlWriterSettings object to allow us to specify the encoding and the indentation for our XML output. Then we wrapped the XmlWriter in a StringWriter which we then used to send our output to the Console window.
We won’t go into too much detail about StringWriters and XmlWriters in this article, but let’s just take from this the fact that it is possible to exert varying levels of control over our XML serialization.
Controlling How We Serialize
Before we jump into deserialization, let’s hop back to our Sample class and take a look at some ways we can control how instances of this type are XML serialized. This sample class has two properties: Name and Value. As it stands now, both properties get serialized when we call the XmlSerializer.Serialize() method. Let’s look at some ways we can control this.
System.Xml.Serialization.XmlIgnoreAttribute
Using the XmlIgnore attribute we can instruct the XmlSerializer to ignore public fields or properties when XML serialization takes place. For this example, let’s add an XmlIgnore attribute to the Value property as shown below:
[XmlIgnore] public Int32 Value { get; set; }
By adding this simple attribute, the XmlSerializer will not serialize this property. Let’s press F5 and give it a try.
When we place a breakpoint and view the XML output in the XML view window, we notice that the Value property did NOT get serialized.

Pretty simple, right?
System.Xml.Serialization.XmlElementAttribute
What if we want to serialize a property, but we want the XmlElement to have a name other than the actual name in the object? This is equally simple by decorating that property with an XmlElement attribute. See the code below.
[XmlElement (ElementName="TheValue")] public Int32 Value { get; set; }
This attribute instructs the XmlSerializer to serialize the property, but to name the XmlElement “TheValue”. Again, we place a breakpoint and view the output:

As we can see in the output, the Value field is serialized as TheValue. Pretty simple.
There are more customization options available through attributes, but we are not going to cover all of them here. This MSDN resource is an excellent source of information.
Deserializing XML with C#
Now that we have discussed XML serialization, let’s look quickly at how to deserialize our XML and re-inflate a Sample object.
We will modify the code in our Program class’s Main() method to look like this:
//let's first instantiate our Sample object //and set its two properties Sample sample = new Sample(); sample.Name = "John Nelson"; sample.Value = 44; //next, create an instance of an XmlSerializer //we will use the Type constructor for this example XmlSerializer serializer = new XmlSerializer(typeof(Sample)); //specify our namespace XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add("jln", "https://johnlnelson.com/namespaces/sample"); //serialize the object to a StringWriter //with our specified namespace StringWriter writer = new StringWriter(); serializer.Serialize(writer, sample, namespaces); //for our subsequent deserialization, we will //get the StringWriter's underlying StringBuilder string xml = writer.GetStringBuilder().ToString(); //we will create a new StringReader using //the xml string created above TextReader reader = new StringReader(xml); //we will deserialize the reader and cast //the resulting object to a Sample type Sample deserialized = (Sample)serializer.Deserialize(reader); //finally, we will write our object to the Console Console.WriteLine("We have deserialized our object"); Console.WriteLine(String.Format("Name: {0}", deserialized.Name)); Console.WriteLine(String.Format("Value: {0}", deserialized.Value.ToString())); Console.WriteLine("That was cool!"); Console.ReadLine();
The commentary in the code is pretty straightforward. We serialized our object into a StringWriter then got that object’s underlying StringBuilder and got its string representation via the xml string object. Then we created a StringReader to utilize the XML string value as an input to our XmlSerializer.Deserialize() method. We immediately cast the return object to the Sample type because we knew that it would work. In most situations like this, should NOT attempt such a cast. Instead, you should stuff the result into an object, then check for null BEFORE attempting the cast. That is just a good programming practice.
When we press F5 to run the application, we see the following output:

In the next article, we will take a quick look at binary serialization.
Serialization with C# and .NET
What is Serialization?
In the world of software development, serialization is the process of converting an object into a format that allows it to either be persisted (saved) to disk or memory, or to be transmitted across a network or over HTTP or TCP.
An object that has been serialized can be reconstructed later which is known as deserialization. I like to think of deserialization as the process by which we re-inflate the object back into its original state. If you think of an XML web service as an example, an application can be written using standard .NET objects, serialized as XML, passed around via HTTP or TCP, then deserialized (re-inflated) by the receiving application and used further.
In this article, we are going to take a quick look at three forms of .NET serialization and talk briefly about each. The following three articles will address XML serialization, binary serialization, and SOAP serialization and provide walkthroughs that give you an overview of how to implement each.
Serialization in .NET
The .NET Framework provides a few serialization mechanisms. Let’s discuss the three most well-known:
- XML Serialization – serializes the public fields and properties of an object into an XML stream. XML serialization does not record or preserve information about the object’s original type or namespace. The .NET Framework provides a class called the XmlSerializer. This class provides methods with which we can serialize an deserialize objects.
- Binary Serialization – serializes an object or an entire hierarchy of objects into a binary format. Binary serialization is a very efficient means of serializing .NET objects. The BinaryFormatter class offers numerous methods allow us to serialize and deserialize objects.
- SOAP Serialization – serializes an object into XML, but also serializes private members. SOAP serialization does not support serialization of generic collections, but the SoapFormatter saves assembly and type information along with the data itself. SOAP serialization is ideal for communications between heterogeneous applications, or applications that are written using different architectures, languages, platforms, etc.
The decision as to which type to use is dictated by the needs of the application. For example, XML and SOAP serialization produce XML output which is usable across multiple platforms. Binary serialization in .NET should only be used in situations where the objects to be serialized and deserialized reside in namespaces that are usable and referenced by each application. If there is only one application in the discussion, then binary serialization will provide a speedy, compact form of serialization that will be quite suitable. So it comes down to considering performance, storage type and location, and extensibility.
See the table below for a high-level comparison of these three types of .NET serialization.

To use serialization in .NET, you essentially only need two things:
- A stream to hold or receive the serialized output
- A formatter (XmlSerializer, SoapFormatter, or BinaryFormatter) to fill the stream with output
As we mentioned earlier, XML Serialization utilizes the XmlSerializer class, binary serialization is provided via the BinaryFormatter, and SOAP serialization is handled by the SoapFormatter – .NET provides very rich support for serialization.
.NET Serialization Walkthroughs
The following posts will dive into the specifics of serialization with C# and .NET. Use the links below to select the appropriate post.
XML Serialization in C# and .NET
System.Reflection – Generic Methods in C# Using Reflection
Generics came on the scene in .NET 2.0 and have been a mainstay ever since! I have developed numerous application frameworks over the years and in every case I have made full use of generics. Very similar to templates in C++, generics allow dynamic type-based operations in a strongly-typed manner and can provide build-time validation. Generics allow us define classes and methods without committing to specific types while we are coding. I am not going to go into a discussion of generics in a general sense since this MSDN resource does a pretty good job of providing a description.
Generic Methods
For our example on to how invoke generic methods via Reflection, we are first going to create a few simple classes whose actual function is a bit nonsensical, but I believe the best way to explain intermediate to advanced concepts is to use simple examples, and thus this is the theme of a large part of my blog :).
- To start, using Visual Studio let’s create a new solution that we will call GenericMethodWalkthroughSolution.
- Next, let’s add two projects to our solution: a C# Class Library which we will call MyClassLibrary and a C# Console Application which we will call MyConsoleApplication.
- In the Console application, add a project reference to MyClassLibrary.
- Right-click the MyConsoleApplication project and selected “Set as Startup Project”.
When we are done with these four steps, our Solution Explorer should look like this:

Next, we will add four classes to our Class Library project. For fun, I am using classes that you may remember from your very first introduction to Object-Oriented Programming (OOP). Don’t laugh though as they are perfect for this discussion!
The classes are outlined below:
- Mammal.cs – a class that will be the base class for the remaining two classes.
- Human.cs – a class that will be instantiated to represent a human.
- Dog.cs – a class that will be instantiated to represent a dog
- Manager.cs – a class that will contain a generic method that will create an instance of a human or a dog, generically and a couple of methods that create either specifically.
We will just add a couple of properties to each of the first three then a single generic method to the fourth.
Since the Mammal class is our base class, let’s define a couple of properties that are common to all mammals. How about Weight and Age? Sounds good to me.
Our Mammal class looks like this:
public class Mammal { public Int32 Weight { get; set; } public Int32 Age { get; set; } }
Our Human class has a couple of properties and inherits Mammal:
public class Human : Mammal { public string Name { get; set; } public String Occupation { get; set; } }
Our Dog class has a couple of properties and inherits Mammal as well:
public class Dog : Mammal { public String Breed { get; set; } public Boolean IsLongHaired { get; set; } }
Finally, our Manager class has a single generic method called Create that is generic and accepts any type that inherits Mammal. See the simple code below:
public class Manager { public T CreateMammal<T>() where T : Mammal, new() { return new T(); } public Human CreateHuman() { return new Human(); } public Dog CreateDog() { return new Dog(); } }
These methods are quite simple, so let’s not focus on their simplicity but focus instead on the concept we’re discussing. The CreateHuman() and CreateDog() methods simply return a new instance of each type. The Create method does the same, but generically and we will move ahead with our examples of how to call generic methods via Reflection.
Let’s return to our Console Application’s Program class and write some code in our static Main() method as shown below.
static void Main(string[] args) { //first, let's just call the methods directly Manager manager = new Manager(); //create a Human Human firstHuman = manager.CreateHuman(); //create a Dog Dog firstDog = manager.CreateDog(); //create a human generically Human secondHuman = manager.CreateMammal<Human>(); //create a dog generically Dog secondDog = manager.CreateMammal<Dog>(); }
I included this initial code simply to illustrate normal invocation of each of the methods. Now we are going to invoke each of the methods via Reflection in the code block below.
static void Main(string[] args) { Manager manager = new Manager(); Type managerType = manager.GetType(); //invoke the CreateHuman method Human human = (Human)managerType.GetMethod("CreateHuman").Invoke(manager, null); //invoke the CreateDog method Dog dog = (Dog)managerType.GetMethod("CreateDog").Invoke(manager, null); //invoke the CreateMammal method and create a Human MethodInfo createMammalMethod = managerType.GetMethod("CreateMammal"); MethodInfo genericCreateMammalMethod = createMammalMethod.MakeGenericMethod(new Type[] { typeof(Human) }); Human genericHuman = (Human)genericCreateMammalMethod.Invoke(manager, null); //invoke the CreateMammal method and create a Dog MethodInfo createMammalMethodDog = managerType.GetMethod("CreateMammal"); MethodInfo genericCreateMammalMethodDog = createMammalMethodDog.MakeGenericMethod(new Type[] { typeof(Dog) }); Dog genericDog = (Dog)genericCreateMammalMethodDog.Invoke(manager, null); }
Okay, the last generic method invocations require some discussion. Let’s break out the first generic invocation of the CreateMammal method in the code block below and insert some commentary to provide an explanation.
//invoke the CreateMammal method and create a Human //As with the other non-generic, Reflection-based invocations in the first two //methods, we first have to get a reference to the MethodInfo object for the CreateMammal method. MethodInfo createMammalMethod = managerType.GetMethod("CreateMammal"); //When invoking a generic method, we have to call the MakeGenericMethod method to tell the runtime //what generic arguments are involved. In this case, we have one argument whose type is Human. //Therefore, we create a new MethodInfo object that contains the substituted types for the generic argument(s). MethodInfo genericCreateMammalMethod = createMammalMethod.MakeGenericMethod(new Type[] { typeof(Human) }); //Instead of using the first MethodInfo object we actually invoke the second (generic) one. Human genericHuman = (Human)genericCreateMammalMethod.Invoke(manager, null);
So the important point here is this: to invoke a generic method via Reflection, you must call the MakeGenericMethod method of the MethodInfo object that holds a reference to the method prior to substitution of the generic arguments. Remember that a generic method may have more than one generic type, so therefore the MakeGenericMethod method accepts an object array that is the collection of generic types.
Now, if we don’t know the generic types at runtime like we did in this example, we can get them through the same MethodInfo object! See the code block below:
MethodInfo method = managerType.GetMethod("CreateMammal"); if (method.IsGenericMethod == true) { Type[] genericArguments = method.GetGenericArguments(); }
If we place a breakpoint on the fourth line of this block and add a Watch to the genericArguments object we can see for ourselves that the generic type is a type that has a BaseType of Mammal. See below:

Related Posts
System.Reflection – Working with the Assembly Class
The objects within the System.Reflection namespace of the .NET Framework provide us a means to get assembly and type information at runtime. Reflection is useful in situations where you need to introspect assemblies and their types, either to gain information or to make use of their functionality dynamically without knowing its specifics in advance. Sounds pretty cool, right? Well it is!
This article will walk you through the basics of Reflection and the Assembly class and will be followed by additional articles that dive much more deeply into the objects within the System.Reflection namespace.
The System.Reflection.Assembly Class
The Assembly class is the primary class within the System.Reflection namespace. This will be our starting point as we take a look at Reflection.
First, we will create a simple Visual Studio solution that we will call ‘ReflectionWalkthroughSolution’.
In this solution, we will add a C# Class Library project which we will call ‘MyClassLibrary’.
Next, we will add a Console Application which we will call ‘MyConsoleApplication’. These names are nonsensical, but this solution is merely for demonstration purposes :).
So, after doing these things, our Solution Explorer should look like this:
MyConsoleApplication is bolded because I have set it as the startup project. This will allow us to hit F5 to debug the app and be able to use the console application as our entry point.
Next, expand MyConsoleApplication and Add a Reference to the MyClassLibrary project. Once this is done we are ready to start writing a little code.
Assembly.GetExecutingAssembly() Method
The first thing we will look at is how to get a reference to the assembly that is currently executing. To do this we will add some simple code in the Main() method of our console application as shown below. After writing this code, place a breakpoint on the line, then hover the thisAssembly object and take a look at the properties of the assembly and their values.
static void Main(string[] args) { //get the currently executing assembly Assembly thisAssembly = Assembly.GetExecutingAssembly(); }
With the code running, when the breakpoint is hit, we can right-click and Add a Watch to the thisAssembly object. When we hit F10 to step over the line, the Watch window is populated with information pertaining to the ‘thisAssembly’ object. See the illustration below.
Hint: Click the image to view it full-size.

Let’s discuss some of the properties that are shown in the Watch window above.
The CodeBase property returns the full path at which the assembly resides. In other words, the execution location for the assembly, including the name of the assembly.dll file itself.
The CustomAttributes property is an IEnumerable<CustomAttributeData> type that displays all of the custom attributes that exist at the assembly level. If you take a look you will see that many of these come directly from the AssemblyInfo.cs file that is found under the Properties folder of the project.

We will dive into custom attributes in a later post. Attributes can be applied to assemblies, classes, properties, methods, and more and through Reflection can be retrieved and read using the appropriate objects. These attributes exist at the assembly level. For now, just be aware that these attributes can be retrieved in this way.
The DefinedTypes property is an IEnumerable<TypeInfo> type that displays all of the types (classes) defined within the assembly. In our simple little console application we only have one defined type. See below.

Looking ahead to later posts, once you have a reference to an Assembly, knowing the types defined in the assembly allow you to dive into the assembly and then into each type as needed.
The FullName property is a string value that displays the full name of the assembly. This is important when discussing a topic such as strong naming.

There are other properties of the Assembly object that are noteworthy for this introduction. They are the GlobalAssemblyCache property which tells you whether or not the assembly is GAC’ed, the ImageRuntimeVersion which tells you the full version of the .NET Framework against which the assembly is compiled, and the MainfestModule which gives specific information about the assembly itself.
Getting Assemblies loaded in the Current AppDomain (AppDomain.Current.GetAssemblies() Method)
Now that we’ve taken a quick look at the properties of an Assembly and how to get a reference to the currently executing assembly, let’s branch out a little and retrieve all of the assemblies referenced and loaded into the current AppDomain. An AppDomain is an isolated environment in which an application executes. The AppDomain class belongs to the System namespace. To retrieve the AppDomain for the currently executing application, we will the AppDomain.Current property.
If we return to the Main() method of the Program class of our little console application and remove the code we wrote originally and replace it with this code:
static void Main(string[] args) { Assembly thisAssembly = Assembly.GetExecutingAssembly(); //Get assemblies loaded in the current AppDomain Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); //create a StringBuilder to hold assembly information StringBuilder sb = new StringBuilder(); //iterate through the array and write info for each assembly foreach (Assembly assembly in assemblies) { //let's create some rudimentary formatted output to show property values //for each assembly sb.AppendLine("================================================================"); sb.AppendLine(String.Format("Assembly: {0}", assembly.FullName)); sb.AppendLine("================================================================"); sb.AppendLine(String.Format("CodeBase: {0}", assembly.CodeBase)); sb.AppendLine(String.Format("Location: {0}", assembly.Location)); sb.AppendLine(String.Format("Number of Types: {0}", assembly.DefinedTypes.Count().ToString())); sb.AppendLine(String.Format("Number of Custom Attributes: {0}", assembly.CustomAttributes.Count().ToString())); sb.AppendLine(String.Format(".NET Runtime Version: {0}", assembly.ImageRuntimeVersion)); //you can add more stuff here to see more properties... } string output = sb.ToString(); }
When we view the formatted output from our StringBuilder, it should look something like this:
================================================================
Assembly: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll
Location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll
Number of Types: 3029
Number of Custom Attributes: 36
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.VisualStudio.HostingProcess.Utilities, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.HostingProcess.Utilities/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.HostingProcess.Utilities.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.dll
Number of Types: 12
Number of Custom Attributes: 17
.NET Runtime Version: v2.0.50727
================================================================
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
Number of Types: 2273
Number of Custom Attributes: 27
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
Number of Types: 303
Number of Custom Attributes: 25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
Number of Types: 2278
Number of Custom Attributes: 29
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.HostingProcess.Utilities.Sync/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities.Sync\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll
Number of Types: 5
Number of Custom Attributes: 18
.NET Runtime Version: v2.0.50727
================================================================
Assembly: Microsoft.VisualStudio.Debugger.Runtime, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.Debugger.Runtime/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Debugger.Runtime.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll
Number of Types: 10
Number of Custom Attributes: 17
.NET Runtime Version: v2.0.50727
================================================================
Assembly: vshost32, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Temp/ReflectionWalkthroughSolution/MyConsoleApplication/bin/Debug/MyConsoleApplication.vshost.exe
Location: C:\Temp\ReflectionWalkthroughSolution\MyConsoleApplication\bin\Debug\MyConsoleApplication.vshost.exe
Number of Types: 1
Number of Custom Attributes: 19
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Core/v4.0_4.0.0.0__b77a5c561934e089/System.Core.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
Number of Types: 929
Number of Custom Attributes: 25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml.Linq/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.Linq.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll
Number of Types: 86
Number of Custom Attributes: 22
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Data.DataSetExtensions/v4.0_4.0.0.0__b77a5c561934e089/System.Data.DataSetExtensions.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Data.DataSetExtensions\v4.0_4.0.0.0__b77a5c561934e089\System.Data.DataSetExtensions.dll
Number of Types: 25
Number of Custom Attributes: 24
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Microsoft.CSharp/v4.0_4.0.0.0__b03f5f7f11d50a3a/Microsoft.CSharp.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.CSharp\v4.0_4.0.0.0__b03f5f7f11d50a3a\Microsoft.CSharp.dll
Number of Types: 316
Number of Custom Attributes: 23
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_32/System.Data/v4.0_4.0.0.0__b77a5c561934e089/System.Data.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll
Number of Types: 1049
Number of Custom Attributes: 21
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll
Number of Types: 1229
Number of Custom Attributes: 25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: MyConsoleApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
================================================================
CodeBase: file:///C:/Temp/ReflectionWalkthroughSolution/MyConsoleApplication/bin/Debug/MyConsoleApplication.EXE
Location: C:\Temp\ReflectionWalkthroughSolution\MyConsoleApplication\bin\Debug\MyConsoleApplication.exe
Number of Types: 1
Number of Custom Attributes: 14
.NET Runtime Version: v4.0.30319
Pretty simple, right? Now let’s dig into the Assembly a little deeper.
Getting Types within an Assembly (Assembly.GetTypes() Method)
Every Assembly can contain one of more Types. We can retrieve all of these types via the Assembly.GetTypes() method. To try this out, we replace the code in our console application’s Program class’s Main() method with this code:
static void Main(string[] args) { Assembly thisAssembly = Assembly.GetExecutingAssembly(); //get all of the Types defined in this Assembly Type[] types = thisAssembly.GetTypes(); //robust code always checks for null FIRST if (types != null && types.Length > 0) { //we'll create a StringBuilder for our formatted output StringBuilder sb = new StringBuilder(); //iterate through the Type[] array foreach (Type type in types) { sb.AppendLine("==============================================================="); sb.AppendLine(String.Format("Type Name: {0}", type.Name)); sb.AppendLine("==============================================================="); sb.AppendLine(String.Format("Type FullName: {0}", type.FullName)); sb.AppendLine(String.Format("Namespace: {0}", type.Namespace)); sb.AppendLine(String.Format("Is it a Class?: {0}", type.IsClass.ToString())); sb.AppendLine(String.Format("Is it an Interface?: {0}", type.IsInterface.ToString())); sb.AppendLine(String.Format("Is it Generic?: {0}", type.IsGenericType.ToString())); sb.AppendLine(String.Format("Is it Public?: {0}", type.IsPublic.ToString())); sb.AppendLine(String.Format("Is it Sealed?: {0}", type.IsSealed.ToString())); sb.AppendLine(String.Format("Qualified Name: {0}", type.AssemblyQualifiedName)); if (type.BaseType != null && !String.IsNullOrEmpty(type.BaseType.Name)) { sb.AppendLine(String.Format("Base Type: {0}", type.BaseType.Name)); } //there are many, many more properties that an be shown... } string output = sb.ToString(); } }
In our console application, there is only one type, the Program class. When we execute the code above we see information for that single type.
===============================================================
Type Name: Program
===============================================================
Type FullName: MyConsoleApplication.Program
Namespace: MyConsoleApplication
Is it a Class?: True
Is it an Interface?: False
Is it Generic?: False
Is it Public?: False
Is it Sealed?: False
Qualified Name: MyConsoleApplication.Program, MyConsoleApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Base Type: Object
Now that we’ve moved down a level and looked at Types within an assembly, let’s move down one more level.
The next post discusses the System.Reflection.Type class and its properties and methods.
Using C# to Manage IIS – Microsoft.Web.Administration Namespace
Introduction
If you have ever attempted to programmatically manipulate IIS in version 6 or earlier, you discovered that it is not a trivial endeavor! You could use Windows Management Instrumentation (WMI), ADSI, or write code to manually manipulate XML. It just wasn’t a task for the faint of heart! I have done it several times and remember it to be quite a headache.
Beginning with IIS 7, programmatic management of IIS has become much simpler with the Microsoft.Web.Administration objects. Using this API model, you can create websites, application pools, applications, virtual directories, etc. with just a few lines of code! Seriously!
The top-level objects of the API are illustrated below.
This post will walk you through some of the features of the Microsoft.Web.Administration namespace objects and provide some real-world examples of how to implement this really cool API.
Getting Started
If you have IIS installed on your development machine, you will be able to find the .dll in Windows\system32\inetsrv folder or you can use the Nuget package. Either way, you will need the Microsoft.Web.Administration.dll file as a reference in your project.
Once you add the necessary reference to your project, you are ready to go!
Working with the ServerManager
To get started, we must first create an instance of the ServerManager class. We do this exactly as you might think:
ServerManager server = new ServerManager();
NOTE: It is important to note that an instance of the ServerManager assumes that the machine on which the application is running is the web server. It is possible to connect to a remote server using the ServerManager, and we will look at that later in this post.
Getting Sites Associated with the Server
Let’s get a list of all of the websites within our ServerManager. To do this, we access the Sites property of the ServerManager instance. The code block iterates each Site object.
ServerManager server = new ServerManager(); SiteCollection sites = server.Sites; foreach (Site site in sites) { ApplicationDefaults defaults = site.ApplicationDefaults; //get the name of the ApplicationPool under which the Site runs string appPoolName = defaults.ApplicationPoolName; ConfigurationAttributeCollection attributes = defaults.Attributes; foreach (ConfigurationAttribute configAttribute in attributes) { //put code here to work with each ConfigurationAttribute } ConfigurationAttributeCollection attributesCollection = site.Attributes; foreach (ConfigurationAttribute attribute in attributesCollection) { //put code here to work with each ConfigurationAttribute } //Get the Binding objects for this Site BindingCollection bindings = site.Bindings; foreach (Microsoft.Web.Administration.Binding binding in bindings) { //put code here to work with each Binding } //retrieve the State of the Site ObjectState siteState = site.State; //Get the list of all Applications for this Site ApplicationCollection applications = site.Applications; foreach (Microsoft.Web.Administration.Application application in applications) { //put code here to work with each Application } }
For more information regarding all of the properties and methods of the Site class, click here.
Getting Applications Associated with each Site
The applications that run under a Site are represented through the Applications property of the Site class. The Applications property is an ApplicationCollection object. We can iterate each Application object with the following code:
ApplicationCollection applications = site.Applications; foreach (Microsoft.Web.Administration.Application application in applications) { //get the name of the ApplicationPool string applicationPoolName = application.ApplicationPoolName; VirtualDirectoryCollection directories = application.VirtualDirectories; foreach (VirtualDirectory directory in directories) { //put code here to work with each VirtualDirectory } }
Getting Virtual Directories Associated with each Application
The virtual directories associated with each Application are accessible via the VirtualDirectories property of the Application class. We can iterate each VirtualDirectory with the following code:
VirtualDirectoryCollection directories = application.VirtualDirectories; foreach (VirtualDirectory directory in directories) { ConfigurationAttributeCollection attribues = directory.Attributes; foreach (ConfigurationAttribute attribute in attributes) { //put code here to work with each attribute } ConfigurationChildElementCollection childElements = directory.ChildElements; foreach (ConfigurationElement element in childElements) { //put code here to work with each ConfigurationElement } //get the directory.Path string path = directory.Path; //get the physical path string physicalPath = directory.PhysicalPath; }
Getting Application Pools Associated with a Server
The ServerManager.ApplicationPools property is an ApplicationPoolCollection object that contains all ApplicationPool objects associated with the server. Remember that this is a server-level property and that an ApplicationPool can be shared by multiple Sites and Applications.
ServerManager server = new ServerManager(); ApplicationPoolCollection applicationPools = server.ApplicationPools; foreach (ApplicationPool pool in applicationPools) { //get the AutoStart boolean value bool autoStart = pool.AutoStart; //get the name of the ManagedRuntimeVersion string runtime = pool.ManagedRuntimeVersion; //get the name of the ApplicationPool string appPoolName = pool.Name; //get the identity type ProcessModelIdentityType identityType = pool.ProcessModel.IdentityType; //get the username for the identity under which the pool runs string userName = pool.ProcessModel.UserName; //get the password for the identity under which the pool runs string password = pool.ProcessModel.Password; }
Now that we have taken a quick look at the object hierarchy and iterated through some of the child collections, let’s switch our view to how to create and manipulate objects.
Creating a New Application Pool
To create a new ApplicationPool, we create a ServerManager instance, then check the ApplicationPools collection to see if our ApplicationPool already exists. If it does, we update a few of its properties, if not we create the new ApplicationPool. Regardless of the whether we are creating or updating, we use the ServerManager.CommitChanges() method.
ServerManager server = new ServerManager(); ApplicationPool myApplicationPool = null; //we will create a new ApplicationPool named 'MyApplicationPool' //we will first check to make sure that this pool does not already exist //since the ApplicationPools property is a collection, we can use the Linq FirstOrDefault method //to check for its existence by name if (server.ApplicationPools != null && server.ApplicationPools.Count > 0) { if (server.ApplicationPools.FirstOrDefault(p => p.Name == "MyApplicationPool") == null) { //if we find the pool already there, we will get a referecne to it for update myApplicationPool = server.ApplicationPools.FirstOrDefault(p => p.Name == "MyApplicationPool"); } else { //if the pool is not already there we will create it myApplicationPool = server.ApplicationPools.Add("MyApplicationPool"); } } else { //if the pool is not already there we will create it myApplicationPool = server.ApplicationPools.Add("MyApplicationPool"); } if (myApplicationPool != null) { //for this sample, we will set the pool to run under the NetworkService identity myApplicationPool.ProcessModel.IdentityType = ProcessModelIdentityType.NetworkService; //we set the runtime version myApplicationPool.ManagedRuntimeVersion = "v4.0"; //we save our new ApplicationPool! server.CommitChanges(); }
In the previous example, we set the ProcessModel.IdentityType to NetworkService. It’s a more common scenario to have a pre-defined service account that is most likely a domain account reserved specifically for this purpose. In this case, we can make a small modification to our code to implement this change. See below.
if (myApplicationPool != null) { //for this sample, we will set the pool to run under the identity of a specific user myApplicationPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser; myApplicationPool.ProcessModel.UserName = UserName; myApplicationPool.ProcessModel.Password = Password; //we set the runtime version myApplicationPool.ManagedRuntimeVersion = "v4.0"; //we save our new ApplicationPool! server.CommitChanges(); }
The UserName and Password values are strings that we would provide to our code when creating the ApplicationPool.
Creating a New Site
Creating a new Site is just as easy as creating a new ApplicationPool!
ServerManager server = new ServerManager(); if (server.Sites != null && server.Sites.Count > 0) { //we will first check to make sure that the site isn't already there if (server.Sites.FirstOrDefault(s => s.Name == "MySite") == null) { //we will just pick an arbitrary location for the site string path = @"c:\MySiteFolder\"; //we must specify the Binding information string ip = "*"; string port = "80"; string hostName = "*"; string bindingInfo = string.Format(@"{0}:{1}:{2}", ip, port, hostName); //add the new Site to the Sites collection Site site = server.Sites.Add("MySite", "http", bindingInfo, path); //set the ApplicationPool for the new Site site.ApplicationDefaults.ApplicationPoolName = myApplicationPool.Name; //save the new Site! server.CommitChanges(); } }
We have only touched on some of the functionality provided to us by the Microsoft.Web.Administration namespace objects. For more information, this MSDN resource is a great place to start. Remember that this API is only valid for IIS version 7.0 and higher and that if you seek to automate a prior version your plan of attack will be quite different.