This documentation has been updated for Themelia Framework 2.0 Beta 2.
In the previous Themelia posts, we've been working only with single-web domain example. In this post, you'll learn what web domains are and how to create them to dramatically increase the flexibility of your web site.
A web domain is a new Themelia 2.0 concept which is somewhat similar to an AppDomain. MSDN describes an AppDomain as follows: "Represents an application domain, which is an isolated environment where applications execute." The keyword here is "isolated" and anyone who has worked intimately work AppDomains you can appreciate their power of isolation.
You often create AppDomains when you want the ability to control what aspect of your application has what functionality (i.e. you cannot unload assemblies from an AppDomain, but you can unload AppDomains). The majority of your application may only have base level functionality, but maybe you want certain financial functionality, perhaps using a plug-in model, in a particular isolated area. AppDomains are exactly what you need. Well, Themelia 2.0 web domain analogously brings that functionality to the web (except you don't "unload" web domains; that makes no sense in a web model.)
With any .NET application there will always be at least one AppDomain. In Themelia, there will always be one web domain, called the "root" web domain. When you install any part of Themelia, you are installing it into a particular web domain. HTTP handlers, preprocessors, aliased handler factories, injection processors, midprocessors, fall through processors, post processors, and poststateprocessors are all installed into web domains, not to your entire web site. However, if you only have one web domain, then functionally these features actually are installed into your entire web site.
When you are using only one web domain, you barely even know you are using one. For example, in the following simple Themelia configuration, you aren't using a web domain directly, so you forget about its existence:
<themelia.web>
<webDomains defaultTarget="/Page_/DefaultPage.aspx">
<add>
<components>
<add key="Security" type="Sample.Web.SecurityComponent, Sample.Web" />
</components>
</add>
</webDomains>
</themelia.web>
However, take a look at following configuration based on the Minima 3.1 sample web site, you can see the web domains very clearly:
<themelia.web>
<webDomains>
<add>
<components>
<add key="Minima" type="Minima.Web.Routing.Component.MinimaComponent, Minima.Web">
<parameters>
<add name="page" value="~/Default.aspx" />
<add name="blogGuid" value="19277C41-7E4D-4AE0-A196-25F45AC48762" />
</parameters>
</add>
</components>
<handlers>
<add matchType="contains" name="PassThrough" text="example.abc" />
</handlers>
</add>
<add name="second" path="/second/" basedOn="root">
<components>
<add key="Minima">
<parameters>
<add name="page" value="~/SecondBlogPage.aspx" />
<add name="blogGuid" value="B23115B1-42E8-46A2-88DE-A56CE505E7D0" />
</parameters>
</add>
</components>
</add>
</webDomains>
</themelia.web>
In this example, you can see that there are two web domains. Minima's core component is installed into the "root" (blank) web domain with one specific configuration and an extra HTTP handler. Minima's core component is then installed into the "second" web domain with a different specific configuration and with no other features installed.
There are many things to notice about this. First, notice that web domains require a name and a path. If there is only one web domain, "root" is assumed, but you may feel free to explicitly set name to "root". If you are using root, the path is ignored. Second, notice the "basedOn" attribute. If you are familiar with WPF, then this should feel natural to you. If not, prepare to learn two things at once.
In WPF, you can create a style for use by various visual entities or by a specific entity type (i.e. TextBox). This style can have various properties set. Your style might, for example, have a property setter for FontSize with a value of 14 and another property setter for BorderThickness with a value of 0. You then create other styles which are based on this style by by setting the "BasedOn" XML attribute, giving the new style all the information from the old. You may then add your own styles to the new one. It's very similar to class-based inheritance in object-oriented programming (OOP).
In the same way, when you base one web domain on another, you essentially copy all of it's contents. In this case, the "second" web domain copies the component information as well as the HTTP handler. You may object by say "No, you completely declared the component again". If you were thinking that, you lose some points. If you look closer, I didn't have to set the component type in the web domain that is based on another web section with a component with the same key.
Also, it's important to note that all the parameters from the component in the "root" web domain copies to the component in the "second" web domain. In the "second" web domain, I'm not setting the values so much as I am resetting them. If I didn't override a parameter, the "root" parameter value would be used. In other words, all inherited parameters are marked as "virtual".
Resetting
Sometimes you may not want to copy all of a web domain's content. Perhaps you want all of it except for one part. Fortunately, with Themelia you have that level of flexibility. You just need to set the reset attribute to true on any part of a web domain which you don't want to copy. In the following example, the "second" web domain doesn't have the PassThrough handler for example.abc as the "root" web domain did above.
<add name="second" path="/second/" basedOn="root">
<components>
<add key="Minima">
<parameters>
<add name="page" value="~/SecondBlogPage.aspx" />
<add name="blogGuid" value="B23115B1-42E8-46A2-88DE-A56CE505E7D0" />
</parameters>
</add>
</components>
<handlers reset="true" />
</add>
With this mechanism you can have HTTP handlers active for one part of your system and completely nonexistent for another. You could even have one handler handle one type of request in one web domain and have a completely different handler work in a different web domain. Just reset that feature before adding your own (for simplicity, you can't remove individual handlers-- that's too much rope!)
Web Domain Inheritance Chain Example
You are allowed to have web domains based on web domains as long own an inheritance chain as you would like. Perhaps you want folder /x/ to have one web domain and /x/y/ with a different web domain. One web domain, for example, may handle the ".svc" extension using a custom service handler while a different web domain may handle the same extension using WCF (which, by the way, is done by setting .svc to use the PassThroughHttpHandler).
As I've mentioned before, all Themelia content is copied from one web domain to another. That ability combined with resetting gives you a tremendous amount of flexibility to control what happens in your entire web site. You can see an example of this in the following configuration snippet:
<themelia.web>
<webDomains>
<add name="finance" path="/finance/">
<aliasedHandlerFactories>
<add type="ABCCorp.Web.Routing.CorporateAliasedHandlerFactory, ABCCorp" />
</aliasedHandlerFactories>
<accessDenials>
<add type="address" text="69.147.112.160" message="Go away" target="" enabled="true" />
</accessDenials>
<handlers>
<add matchType="endsWith" name="ReportCreator" text=".xls" />
</handlers>
</add>
<add name="sales" path="/finance/sales/" basedOn="finance">
<accessDenials reset="true" />
</add>
<add name="marketing" path="/marketing/" basedOn="sales">
<handlers reset="true">
<add matchType="endsWith" name="ConverstionTracking" text="/tracking/" />
</handlers>
</add>
</webDomains>
</themelia.web>
In this example we have four web domains: "root", "finance", "sales", and "marketing". Under the /finance/ branch we are watching for any ".xls" access, sending requests to the ReportCreator handler. We are also blocking any access from a specific IP address. Under the /finance/sales" branch, like under the /finance/ branch, we are monitoring for ".xls". However, we aren't blocking any access. Finally, under the /marketing/ branch we aren't watching for ".xls" access nor are we blocking any addresses, however, we are looking for any access under "/marketing/tracking/", sending it to the ConverstionTracking handler. Finally, these are all sharing the same aliased handler factory to allow http handlers aliasing in a centralized location.
Links