2007-12-14

A code story staring MVP.NET - Environment

Previously

A code story staring MVP.NET - Introduction

Development Environment

Even in a solo developer project, I believe a source control system is extremely important. There are several free options to choose from (A few of them are SourceForge, CodePlex and Google Code), I created a new Google Code Project called phogaldotnet with LGPL as license.

googlecode

Ideally I would want a Build Server and do Continuous Integration, but since I don't have a server, I can only set it up on my own machine. This is less optimal for a number of reasons (dependencies are already on my machine, its not online all the time etc.), so I have decided to skip this.

Tools

To start I will only be using the following tools:

VS 2008

Tortoise SVN

If/when new tools are relevant, I will install them.

Mappings

I usually keep all OSS projects in this folder "c:\oss\", so I have created a folder for "phogaldotnet" so the full path is "c:\oss\phogaldotnet", and done "SVN Checkout" from "https://phogaldotnet.googlecode.com/svn/trunk/".

Additional installation

Download and install ASP.NET Extension 3.5 Preview for full VS 2008 support.

Solution and project structure

I like to have a folder called "Dependencies"(some call it "bin") with all the 3rd party dll's.

Also I downloaded the MVCToolkit and copied the dll and pdb files to the Dependencies folder.

A a SVN Add on the Dependencies folder and then a SVN Commit.

svndependencies

Creation of the Visual Studio solution and MVC project.

vssolution

SVN Add (not the bin and obj folder) and SVN check in.

Namespaces

I named the solution "PhogalDotNet" and the web project "PhogalDotNetWebAccess", because I usually don't like sticking everything into 1 namespace (i.e."PhogalDotNet"). Application and components are two different things. Application is a single use of components configured for a specific need, and components are self contain units of logic that any number of applications can use.

The idea is that I want a separate project with the name "PhogalDotNet", that contains all the logic. I will hopefully end up with an application, that is consists of a web.config file with HTTPHandlers and HTTPModules and a connection string to a database. So I will slowly be refactoring towards that.

More than that, I don't like when types in a namespace reference types in deeper namespace. Ex.: "Root.Core" types should not reference types in "Root.Core.Specialized". But a reverse dependency from "Root.Core.Specialized" to "Root.Core" is fine.

I am even suspicious about cross references within the same root. Ex.: "Root.FeatureA" should not reference "Root.FeatureB".

Morten Lyhrs rules for namespaces:

  1. Don't mix application and component namespace root names.
  2. Every application should have it's own namespace root.
  3. Components can share a root.
  4. Types in a namespace should not reference types in a namespace below it's own.
  5. No cross references within the same namespace root.

For simple components I usually end up with the following namespace structure:

"ComponentRoot"

"ComponentRoot.Infrastructure"

"ComponentRoot.Infrastructure.Facade"

All types in "ComponentRoot" are POCO, I try to keep this as fat as possible. Ideally all if/else, loops and as much logic as possible should live here. Unit test(Fast tests) is min. 100% coverage! Extension points for non logic are created via interfaces or virtual/abstract methods.

Types in "ComponentRoot.Infrastructure" is the default infrastructure provided. It implements the extension points in in "ComponentRoot". Ex.: "ComponentRoot.MailService" interface is implemented by "ComponentRoot.Infrastructure.SmtpService". Unit tests don't make much sense here, but integration tests(Slow tests) do.

If "ComponentRoot.Infrastructure.Facade" is used directly it might be a DSL, else it could be a implementation for a given framework (HTTPModules and HTTP Handlers come to mind). This is the classic "Service Layer".

Anyone can extend the component with their own infrastructure, but the core logic is unmodifiable and rigorously tested.

2007-12-13

A code story staring MVP.NET - Introduction

Why

I want to write a journal where all the decisions, problem evaluation and errors are a part of the story. So instead of focusing on the end result, this code story will focus on the journey. Actually there might never be a end result or the end result could/will change during the journey.

How

Instead of inventing a problem domain, I choose to make a photo gallery. Wife Lyhr and you(the reader) will act as customers, and your feedback decide where the journey goes.

What
Version 1.0
Goals Requirements
Privacy  
  Authentication
  Authorization
  Users
  Groups
  Roles
Content discoverability  
  Albums
  Labels
  Metadata
  Searchable
  Easy human direct access
Community feedback  
  Comments
  Rating
  Favorites

 

Goals should be fairly static, and all requirements should fit into a goal. Every code story episode will break down a requirement into specifications, the specification might never be implemented or change any number of times.

Technical Restrictions
  • The project is developed in .NET, and the code is written in C#.
  • A webserver running ASP.NET MVC.NET is the main interface for the user, and the webserver owns all data. No other application can have direct access to any data.
  • Photos can be placed in a database or in a folder.
  • Must support multiple databases (MSSQL and MySQL as a minimum).
Project Name

Every project should have a name, and I am usually very bad at picking them. Since its based on the .NET framework, and one of the top domains is .net, I choose the name phogal.net (short for "Photo Gallery Dot Net").

Quality

The output of this should be production quality, and you can hopefully help/guide me when I am on a wrong track. I not saying that everybody will be happy with my decisions, but I will try to explain why I choose as I do. Debate about the decisions are more than welcome.

Next

A code story staring MVP.NET - Environment

2007-12-12

How to get syntax highlighting in Blogger with SyntaxHighlighter

Syntaxhighlighter is a cool JS library for syntax highlighting on a web page.

  1. Download the latest version
  2. Unrar to a temp folder. ex: c:\temp\syntaxhighlighter
  3. Transfer the folders (Scripts and Styles) to a web server. I use google code to host my files.
  4. Open blogger.com and sign in.
  5. Goto "Template" and choose "Classic" layout.
  6. Goto Edit Html.
  7. After <head> insert the following:
    <link href="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></link>
    <script language="javascript" src="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/shCore.js"></script>
    <script language="javascript" src="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/shBrushCSharp.js"></script>
    <script language="javascript" src="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/shBrushXml.js"></script>
    <script language="javascript" src="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/shBrushCss.js"></script>
    <script language="javascript" src="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/shBrushJScript.js"></script>
    <script language="javascript" src="http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/shBrushSql.js"></script>

  8. And before </body> insert:
    <script language="javascript">
      dp.SyntaxHighlighter.ClipboardSwf = 'http://mortenlyhr.googlecode.com/svn/trunk/SyntaxHighlighter/Scripts/clipboard.swf'; 
      dp.SyntaxHighlighter.HighlightAll('code'); 
    </script>
    

2007-12-11

How to restore to another database

That is take a backup of database A and restore the .bak file to database B.

By far the easiest way is to do it in script:

restore database myDatabase from disk = 'C:\mybackup.bak' with replace

No more opening of SSMS (yawn), replacing file paths and remember to set a overwrite flag.

2007-12-10

How to turn of JIT optimizations on a release compiled exe

Basically create a ini file with the same name as the exe with the following content

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

Perhaps if an unrecoverable errors occurs in a program, it can create the ini file and restart the application. The user can the redo the action(s) to create the error, and a full-stack trace can be created.

2007-10-31

How to insert an assembly version number into a WiX v3 script using TFS

The following blog post shows how to do it, but I though it was a bit clumsy.

First set the product version to a var in the .wxs file:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="PUT-PRODUCT-NAME-HERE" Language="1033" Version="$(var.PRODUCTVERSION)" Manufacturer="PUT-COMPANY-NAME-HERE" UpgradeCode="PUT-GUID-HERE">
    <Package InstallerVersion="200" Compressed="yes" Id="*"/>
    <Media Id="1" Cabinet="WixProject.cab" EmbedCab="yes" />
    <!--The rest of the WiX declaration.-->
  </Product>
</Wix>

Next define the constant on the project.

WiX Project -> Properties -> Compiler Tab -> Define constants -> "PRODUCTVERSION=1.0.0.0;".

Do this for both debug and release, so a local build is always 1.0.0.0.

Is recommended that the WiXProject is not build locally (just remove it from the solution configuration manager).

Now we want TFS to make us a build where all project assemblies are given an incremental version and the MSI is versioned with the same version number.

Install AssemblyInfoTask on the Build Server. (See this blog for a fix for y7k issue.)

In TFSBuild.proj add the following:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- TO EDIT BUILD TYPE DEFINITION
To edit the build type, you will need to edit this file which was generated
by the Create New Build Type wizard. This file is under source control
needs to be checked out before making any changes.
The file is available at -
$/{TeamProjectName}/TeamBuildTypes/{BuildTypeName}
where you will need to replace TeamProjectName and BuildTypeName with your
Team Project and Build Type name that you created
Checkout the file
1. Open Source Control Explorer by selecting View -> Other Windows -> Source Control Explorer
2. Ensure that your current workspace has a mapping for the $/{TeamProjectName}/TeamBuildTypes folder and 
that you have done a "Get Latest Version" on that folder
3. Browse through the folders to {TeamProjectName}->TeamBuildTypes->{BuildTypeName} folder

4. From the list of files available in this folder, right click on TfsBuild.Proj. Select 'Check Out For Edit...'
Make the required changes to the file and save
Checkin the file1. Right click on the TfsBuild.Proj file selected in Step 3 above and select 'Checkin Pending Changes'
2. Use the pending checkin dialog to save your changes to the source control
Once the file is checked in with the modifications, all future builds using
this build type will use the modified settings
-->
<!-- Do not edit this -->
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v8.0\TeamBuild\Microsoft.TeamFoundation.Build.targets" />

<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.Targets"/>
<ProjectExtensions>
<!--  DESCRIPTION
The description is associated with a build type. Edit the value for making changes.
-->
<Description>
</Description>
<!-- BUILD MACHINE
Name of the machine which will be used to build the solutions selected.
-->
<BuildMachine>****</BuildMachine>
</ProjectExtensions>
<PropertyGroup>
<!--  TEAM PROJECT
The team project which will be built using this build type.
-->
<TeamProject>****</TeamProject>
<!-- BUILD DIRECTORY
The directory on the build machine that will be used to build the
selected solutions. The directory must be a local path on the build
machine (e.g. c:\build).
-->
<BuildDirectoryPath>****</BuildDirectoryPath>
<!-- DROP LOCATION
The location to drop (copy) the built binaries and the log files after
the build is complete. This location has to be a valid UNC path of the
form \\Server\Share. The build machine service account and application
tier account need to have read write permission on this share.
-->
<DropLocation>\\appfrontend02\TFSBuildDrops\EVServiceBil</DropLocation>
<!-- TESTING
Set this flag to enable/disable running tests as a post build step.
-->
<RunTest>****</RunTest>
<!-- WorkItemFieldValues
Add/edit key value pairs to set values for fields in the work item created
during the build process. Please make sure the field names are valid 
for the work item type being used.
-->
<WorkItemFieldValues>Symptom=build break;Steps To Reproduce=Start the build using Team Build</WorkItemFieldValues>
<!-- CODE ANALYSIS
To change CodeAnalysis behavior edit this value. Valid values for this
can be Default,Always or Never.
Default - To perform code analysis as per the individual project settings
Always - To always perform code analysis irrespective of project settings
Never - To never perform code analysis irrespective of project settings
-->
<RunCodeAnalysis>****</RunCodeAnalysis>
<!-- UPDATE ASSOCIATED WORK ITEMS
Set this flag to enable/disable updating associated workitems on a successful build
-->
<UpdateAssociatedWorkItems>true</UpdateAssociatedWorkItems>
<!-- Title for the work item created on build failure -->
<WorkItemTitle>Build failure in build:</WorkItemTitle>
<!-- Description for the work item created on build failure -->
<DescriptionText>This work item was created by Team Build on a build failure.</DescriptionText>
<!-- Text pointing to log file location on build failure -->
<BuildlogText>The build log file is at:</BuildlogText>
<!-- Text pointing to error/warnings file location on build failure -->
<ErrorWarningLogText>The errors/warnings log file is at:</ErrorWarningLogText>
</PropertyGroup>
<PropertyGroup>
<!-- Assembly version properties. Add others here -->
<AssemblyMajorVersion>1</AssemblyMajorVersion>
<AssemblyFileMajorVersion>1</AssemblyFileMajorVersion>
<!-- TF.exe -->
<TF>&quot;$(TeamBuildRefPath)\..\tf.exe&quot;</TF>
<!-- AssemblyInfo file spec -->
<AssemblyInfoSpec>AssemblyInfo.cs</AssemblyInfoSpec>
</PropertyGroup>
<!-- Set this to non-existent file to force rebuild. -->
<ItemGroup>
<IntermediateAssembly Include="$(SolutionRoot)\foobar.dll"/>
</ItemGroup> 

<ItemGroup> <!-- SOLUTIONS The path of the solutions to build. To add/delete solutions, edit this value. For example, to add a solution MySolution.sln, add following line - <SolutionToBuild Include="$(SolutionRoot)\path\MySolution.sln" />

To change the order in which the solutions are build, modify the order in which the solutions appear below. --> <SolutionToBuild Include="****" /> </ItemGroup> <ItemGroup> <!-- CONFIGURATIONS The list of configurations to build. To add/delete configurations, edit this value. For example, to add a new configuration, add following lines - <ConfigurationToBuild Include="Debug|x86"> <FlavorToBuild>Debug</FlavorToBuild> <PlatformToBuild>x86</PlatformToBuild> </ConfigurationToBuild> The Include attribute value should be unique for each ConfigurationToBuild node. --> <ConfigurationToBuild Include="Release|Any CPU"> <FlavorToBuild>****</FlavorToBuild> <PlatformToBuild>****</PlatformToBuild> </ConfigurationToBuild> </ItemGroup> <ItemGroup> <!--  TEST ARGUMENTS If the RunTest is set to true then the following test arguments will be used to run tests. To add/delete new testlist or to choose a metadata file (.vsmdi) file, edit this value. For e.g. to run BVT1 and BVT2 type tests mentioned in the Helloworld.vsmdi file, add the following - <MetaDataFile Include="$(SolutionRoot)\HelloWorld\HelloWorld.vsmdi"> <TestList>BVT1;BVT2</TestList> </MetaDataFile>

Where BVT1 and BVT2 are valid test types defined in the HelloWorld.vsmdi file. MetaDataFile - Full path to test metadata file. TestList - The test list in the selected metadata file to run.

Please note that you need to specify the vsmdi file relative to $(SolutionRoot) --> <!-- <MetaDataFile Include=" "> <TestList> </TestList> </MetaDataFile> --> </ItemGroup> <ItemGroup> <!--  ADDITIONAL REFERENCE PATH The list of additional reference paths to use while resolving references. For example, <AdditionalReferencePath Include="C:\MyFolder\" /> <AdditionalReferencePath Include="C:\MyFolder2\" /> --> </ItemGroup> <Target Name="AfterGet" Condition="'$(IsDesktopBuild)'!='true'"> <!-- Set the AssemblyInfoFiles items dynamically --> <CreateItem Include="$(SolutionRoot)\**\$(AssemblyInfoSpec)"> <Output ItemName="AssemblyInfoFiles" TaskParameter="Include" /> </CreateItem> <Exec WorkingDirectory="$(SolutionRoot)" Command="$(TF) checkout /recursive $(AssemblyInfoSpec)"/> </Target>

<Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'"> <!-- Create MSI--> <Exec WorkingDirectory="$(SolutionRoot)" Command="C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe $(SolutionRoot)\SolutionFolder\WixProjectFolder\WixProject.wixproj /target:rebuild /p:Configuration=Release /p:DefineSolutionProperties=false /p:OutputPath=$(OutDir) /p:DefineConstants=%22PRODUCTVERSION=$(MaxAssemblyVersion);%22" Timeout="60000" /> <Copy SourceFiles="$(OutDir)\msiname.msi" DestinationFolder="$(OutDir)" /> <Exec WorkingDirectory="$(SolutionRoot)" Command="$(TF) checkin /comment:&quot;Auto-Build: Version Update $(MaxAssemblyVersion) ***NO_CI***&quot; /noprompt /override:&quot;Auto-Build: Version Update&quot; /recursive $(AssemblyInfoSpec)"/> </Target> <!-- In case of Build failure, the AfterCompile target is not executed. Undo the changes --> <Target Name="BeforeOnBuildBreak" Condition="'$(IsDesktopBuild)'!='true'"> <Exec WorkingDirectory="$(SolutionRoot)" Command="$(TF) undo /noprompt /recursive $(AssemblyInfoSpec)"/> </Target> </Project>

Basically it runs the WixProject in a separate MSBuild process, this also we can create any number of msi files we want with different configurations.

Remember if you use CI, you have to stop an infinet loop because the Build Script does a check-in on the assemblyinfo.cs files.
I use Automaton, that fails if a running build gets a build request. So that work just fine.

2007-10-26

WiX v3 and detection of IIS and ASP.NET

First part of the problem is not WiX specific, namely detection of various Windows Components.
The right place to look is in the Windows Registry, and this article even explains where.

Next we need to create a WiX property that seaches a registry value, and create a WiX Condition that verifies that the property has a given value.

An Example that detects .NET 2.0, IIS and ASP.NET 2.0 are installed and configured in Windows.

<Condition Message="This setup requires the .NET Framework 2.0 or higher.">
  <![CDATA[MsiNetAssemblySupport>="2.0.50727"]]>
</Condition>

<Condition Message="This setup requires the IIS windows component is installed.">
  <![CDATA[IIS="#1"]]>
</Condition>

<Condition Message="This setup requires the ASP.NET 2 is configured in IIS.">
  <![CDATA[ASPNET2]]>
</Condition>

<Property Id="IIS">
  <RegistrySearch Id="IISInstalledComponents" Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OC Manager\Subcomponents" Type="raw" Name="iis_common" />
</Property>

<Property Id="ASPNET2">
  <RegistrySearch Id="ASPNET2InstalledComponents" Root="HKLM" Key="SOFTWARE\Microsoft\ASP.NET\2.0.50727.0" Type="raw" Name="Path" />
</Property>

How to compare 2 tables in SQL Server

Jeff has a great post titled The shortest, fastest, and easiest way to compare two tables in SQL Server: UNION !

No joins or select in. Just pure UNION magic.

How to clear SQL Server query cache

If you execute the same query two or more times in a row, the time it takes to complete might vary. If you are the only user on the database, it is because of the build in query cache.

The cache can be cleared with the following script:

DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE