This is a continuation of the "Best Practices: Development Team Collaboration and DotNetNuke" multi-part series of blogs. If you have not read Part 1: The Development Environment, Part 2: The SqlDataProvider Files, you may wish to do so before reading this entry. The second installment covered not only the actual SqlDataProvider files, it also covered how to use those files to keep database structure synchronized for all developers collaborating on a project. This installment will cover some basic project structure, primarily focused on the WAP project structure, and also how to manage references for your team collaborating on a single project.
If you are not familiar with the Web Site Project (WSP) structure or the Web Application Project (WAP) structure you may want to read the first few paragraphs in a blog post I stumbled upon today. The blog post also contains several links that can help you understand the difference between the two models. The blog post as well as the links it references are a few months behind this blog post, therefore, they were all posted prior to Visual Studio 2008 Service Pack 1 release. Prior to this Service Pack release, no Visual Studio Express editions supported class libraries or web application projects. If you are working in environments that use Visual Studio 2005 and 2008 and therefore require multiple solution files you may want to check out this Rick Strahl, who's opinion I share in regards to WAP vs WSP, blog post to avoid issues (affects those without Visual Studio 2008 installed). I don't want to spend any time debating WAP vs. WSP but I will note a few reasons why we choose WAP: Faster compilation during development, fewer projects to load when loading up Visual Studio, discourages developers from making core changes in the DotNetNuke web site project, and keeps source files off the production web servers. I have focused on WAP and WSP project types first because its the first question you need to answer when creating a new project. Please note that if you really want to use WSP over WAP, many of the items discussed in this post still apply.
Take a look at the screen shot to the left of this text. This is the DotNetNuke Forum's Visual Studio 2008 project properties Web section (which also looks the same in 2005). The Forum, like any AppTheory module, uses IIS for debugging. If you chose WSP over WAP, this is not a concern as it will be handled via the DotNetNuke solution file you are using. The reason we use IIS instead of the built in Visual Studio Development Server, VSDS formerly known as Cassini, is to mimic the production environment as much as possible even on the local developer machines. One major difference between applications running in IIS vs. VSDS and thus development environment vs production: security context. Security context can be broken down into three affected areas: Other resources required by pages in the application, Database access, Code access security. While I strongly advise that anyone working on DotNetNuke modules beyond the hobbyist level use IIS for debugging, there are a couple of downsides to using IIS. The first is no support for Edit and Continue, which means all code changes while debugging require the entire application restart prior to taking affect. The other is that all developers must run a Windows operating system that includes IIS.
Back to the screen shot, the Start URL is set for the default DotNetNuke project's virtual directory URL http://localhost/DotNetNuke_2. The "Use Local IIS Web server" radio button is selected which enables the Project URL text box. Take note that the Project URL MUST be pointed to the URL where your project file resides, in the case of the DotNetNuke Forum module this is located at http://localhost/DotNetNuke_2/DesktopModules/Forum. If you have this set to the root of the web site, such as http://localhost/DotNetNuke_2, you will see that Visual Studio wants you to create a virtual directory (see details about that problem here) which may or may not already exist. The final import setting shown in the screen shot is the "Apply server settings to all users" which, as it says in parentheses, stores the information in the project file. Checking this item is important when collaborating with your development team as it will provide some consistency because the file will be shared across all developers and stored in source control. This also aids in time spent in getting new team members up in running.
Project Structure
Project structure does differ between WAP and WSP project types. Since we do not support WSP project types, this section is targeted for WAP project types. However, one of the structure related items that does not change is where your module or provider will reside. You should always develop your modules in the web site's DesktopModules folder. It is also generally a good idea to nest your custom modules within a sub-folder of the DesktopModules folder that represents you our your company. At AppTheory, we always use a directory named AT. Using the nested AT folder groups all custom modules developed by our company within that single folder. Outside of the logical grouping, the sub-folder doesn't really offer much of an advantage beyond that outside of the likelihood of your custom module clashing with the name of another is somewhat decreased. If you are using Subversion as your source control system, you can also benefit by using a sub-folder when working with Externals but that is a conversation for another day. Please note that if you are working on provider projects, these should be placed within the web site's Providers folder and the same concepts still apply. Also keep in mind that Skin Objects are very similar to modules so the same concepts also apply here too.
With the proper directory chosen based on the project you are going to create (ie. module or provider), the final decision to make when creating a new project is the name of the project. Myself and many other members of the DotNetNuke core team have always recommend naming your module and the folder it is contained in CompanyName.ModuleName. Doing this should help in avoiding clashes with other custom modules developed by other developers or companies. It has also been recommended in the past that the name of the generated assembly also mimic this naming convention or something similar. Typically, I prefer to leave the words Module and Provider out of the project name, namespace and the assembly name in addition to the folder name but that is not a requirement.
If you are building a module or a provider that is going to store data in the database besides module settings, which most do, you will need some type of data provider to accompany your project. Regardless of Entity Spaces usage, Core DAL, or LLBLGen, you will always have to use the SqlDataProvider files discussed in Part 2 and therefore need to have them contained within your project. This should be stored within your module or provider directory within Providers/DataProviders/SqlDataProvider, unless of course you are using another data server such as Oracle in which case the final folder would read OracleDataProvider. Sometimes it is recommended to actually create a separate project for your data provider if you are integrating unit testing although it is not absolutely necessary to do so. Unit testing is a another discussion that I am tabling for another day.
While most DotNetNuke modules will have additional common folders in almost all projects such as Components, Images, Controls, etc, one that every module should have is App_LocalResources.
This is the folder that contains all the localization related resource files used by DotNetNuke during runtime. If you are not familiar with the XML .resx files or localization, you may want to investigate these outside of this blog post but to provide a 30k foot over view: These XML files should be named identical to their .ascx or .aspx counterparts and they contain resource key's that have a value set that is specific to a particular locale such as US English. If a nested folder contains .ascx files, these nested folders should also contains the App_LocalResources folder that has corresponding .resx files within it. If a module follows this concept, the module's static content can be set per locale available in DotNetNuke installs by simply adding language packs to the DotNetNuke installations and site user's changing their locale preference (Default is en-US). Please note that a user's locale also determines which time format is used throughout the core portal and in properly coded modules.
An example of the DotNetNuke Forum module with the German language pack installed can be seen in the screen shot on the right. There are a few things to take note of here. The first item worth noting is the naming convention. The locale for the German language using the country of Germany (aka Deutsch and Deutschland) results in FileName.ascx.de-DE.resx. Second, the de-DE local .resx files are not part of the project. This is because they are not installed with the module. Even if you are providing additional locale's for your custom module, you should keep excluded from the project and distribute them separately like DotNetNuke core modules do using language packs. If you are using WSP, there is no option to exclude the files.
References
References can be a real pain for developers. Not only are there several types of references (Project references, assembly references, web references), there are also various locations where items are referenced from such as the web site's root bin directory or for some third party component vendors which sometimes store assemblies in the in the Program Files folder. Of course, having referenced items outside of source control makes it hard to ensure all developers collaborating are using the same version of a referenced assembly. Because of this, references in the Program Files folder is out of the question. Also related to source control, no items including referenced assemblies should be located in the web site's root bin directory unless necessary for the application to start. For example, the DotNetNuke core library project compiles into the DotNetNuke.dll. This is required for DotNetNuke to start so it must be located in the web site's root bin directory. Please keep in mind that I also feel strongly that no changes should occur in the core DotNetNuke Library project so keeping this assembly in source in the web site's root directory is perfectly acceptable. In some cases, however, it is best to isolate referenced assemblies in a separate folder outside of the root bin directory. This way, all developers collaborating always know where to reference assemblies such as third party components. We recommend creating a folder within the Library folder of DotNetNuke distributed with all core distributions that include source (ex. Library\AT\).
As for which type of reference to choose, this will of course vary based on your needs. If you are referencing a third party component, such as those provided by Telerik, you should use an assembly reference. When referencing core DotNetNuke items, you should also use an assembly reference when working with WAP. Some situations, however, require you to use project references. We typically only use project references when working with additional assemblies that will be distributed with our module or provider.
There are a few other items for your consideration when discussing the topic of references. One of those is version dependency. It should go without saying, the version of DotNetNuke you compile your module or provider against is the minimum version of DotNetNuke your project will be able to run on. In other words, a module compiled against 4.6.0 cannot run on any previous version of DotNetNuke such as 4.5.3. However, a module compiled against 4.6.0 should run in 4.9 so long as there are no API breaking changes in DotNetNuke (it is the general practice of DotNetNuke to not make breaking changes). Sticking with versions for a moment, when working with WAP project types you must also be more careful in regards to version errors across shared third party components such as those made by Telerik. For example, if two separate modules (A & B) depend on the same Telerik component, it is quite possible that installing module A that depends on this component and it includes it in its installation package will result in breaking of the other module B. This would occur only if the module B depended on a previous version of the Telerik component. In this case, the only way to resolve the problem is to update module B or revert module A back to the same version of the Telerik component that module B depends on.
Well, that about wraps up the basics around team collaboration and project/reference structure. If you need to view an example of everything discussed here, head on over to the DotNetNuke web site and download the latest Forum release. While it won't mimic this discussion to a T, because it is a core module and not associated with a specific company or third party components, it will follow all other conventions discussed in this blog post. In Part 4 I plan on focusing on the .dnn manifest file as well as deployment.