This is good: http://ftponline.com/weblogger/forum.aspx?ID=18&DATE=01/13/2006#505

Just saw this post by Jan Schreuder talking about MSDN Virtual Labs. If you haven't checked these out yet, it's worth it. Without installing the software, try out various Microsoft technologies, both present and future. If you were just dying to try out that new SQL Server 2005 feature, or find out what Windows Workflow Foundation is really all about... look no further than MSDN Virtual Labs!

From what I can tell, MS has set up some boxes with Virtual Server hosting images of pre-built machines containing various technology combinations. You use the web client to RDP to these machines, providing a reasonable remote control experience. You actually get administrator rights to the machine, allowing you freedom to try various things without being hampered by security. And I'm guessing that the machines are set up in undo mode and don't save state - so that the next person gets the same baseline machine as you started with. Cool!

In "ADO.Net 2 Advanced Data Access Patterns", Pablo Castro demonstrates the workings of an algebraic query processor, built on top of the ADO.Net technology. I guess it is technical, but his point is that it is possible, and actually not that difficult. I find it interesting to see the methods of logic processing that were presented in the advanced sessions this week - I've had to write some of these things before, but realize that I don't really understand the standard theories behind code and logic compilation. Good way to keep me humble!

One interesting thing that Pablo described was the indexing that happens behind the scenes in ADO.Net DataTables. If you run DataTable.Select, an index is automatically created on the data to optimize the select. Likewise, a DataView triggers the indexing - I suppose it is probably the same thing happening behind the scenes, but the DataView gives you a reference to the filter/sort request that was made, so that it is easy to re-execute it (though the creation of a new DataView with the same parameters would use the indexes created by the previous one)

Posted by Joshua Langemann | 7 comment(s)
Filed under:

I'm too young for this. A bunch of industry-leading language developers and university professors talking about dynamic languages with enough acronyms and language names to share a stick at! I have an interest in the progress of IronPython, so the title "Scripting and Dynamic Languages in the CLR" attracted my attention, especially seeing that Jim Hugunin was one of the panelists. They also had key members (perhaps leaders) of the Monad and VB projects from Microsoft, and a non-MS guy developing a language called Boo.

Questions were varied but there was this one statement that is worth remembering: "Computer Science is the one area where you can predict the future - just look back 20 years" - the theory being that 20 years ago men had these ideas, but only now do we have the horse-power to actually implement them. After all, most of the "cool new features" that we have spent the conference talking about are really old concepts that we are only now implementing, because the industry and technological capabilities are ripe for them. In reality, isn't it true at some level that nothing is really new - in many aspects we just keep repeating history on various levels.

Posted by Joshua Langemann | with no comments
Filed under:
Quick tidbit learned in instructor-led class - Application.DoEvents() will cause UI to be updated. You can call this in the middle of a long-running process. For example, if I am going to process a bunch of data, and want to update status/progress information, the "cheaters" way to do it is to update these controls during the process, and then call Application.DoEvents() to fire the UI events so that the updates are visible. Otherwise, the UI isn't updated until the long-running process completes - which is kind of pointless. Of course, the right way to do this is with worker threads and Control.Invoke...
Posted by Joshua Langemann | 4 comment(s)
Filed under:

This is almost unbelievable. I'm sitting in a session by Andres ("father" of C#) describing the future Language INtegrated Query implementation in C#. Here is the sample that he's dealing with (just to prove that the features go beyond just databsae queries)

var q =

from m in typeof(string).GetMethods()

where !m.IsStatic orderby mj.Name

group m by m.Name into g

select new {Method = g.Key, Overloads = g.Group.Count() };}

Lots of new stuff here to make this work - from the "var" feature to type inference based on a curly-braced list of values, to dynamic type extensions... Anyways, to say the least, I'm floored by the possibilities throughout the languages (C# and VB...) that this will provide. Next session is on C# 3 - I guess this will all make more sense then. Lots of new keywords, though... not sure if that is a good thing. But they are apparently all calls to methods behind the scenes, allowing full extensibility. Sounds almost scary!

Posted by Joshua Langemann | 30 comment(s)
Filed under:
Here is the simple Email design for the Monday Framework Design precon that Jim Beveridge and I worked out:
 
Samples:

// send a simple text message
Email.SendMessage("bob@mybusiness.com", "Hey Bob, how's it going?");

// send an e-mail with attachments
Email email = new Email();
email.AddFile("CoolPic.jpg");
email.Message = "Hey Bob...";
email.Send("bob@mybusiness.com ");

Class Design:
 
public class Email
{
    public static void SendMessage(string sendTo, string message) {}

    public Email() {}

    public string Message
    {
        get;
        set;
    }

    public void AddFile(string fileName) {}

    public void Send(string sendTo) {}

}

In hindsight, a few comments (and we'll see what the experts say...)

  • A "From" property or argument may have been helpful, if we can't infer who the message is coming from
  • We purposely ommitted CC, Subject, and BCC, because they weren't specifically requested in the requirements
  • "To" is necessary in order to send, so we enforced that by making it part of the methods used to send the e-mail - could have made it a property with exception logic in the Send method, I suppose
  • Tried to handle the simple case in a simple way, by providing static SendTo method
  • Should probably deal with streams instead of just file names, but this provided a really simple way to handle attachments, and the goal was simplicity
  • Once again, in order to provide simplicity we didn't handle multiple "To" strings - we could have used a collection or something, I suppose
Posted by Joshua Langemann | with no comments
Filed under:

Today's sessions of choice was "The Art of Building a Reusable Class Library" - Brad has posted so much about this that I just had to attend. One great thing about this session is that they gave away copies of their book "Framework Design Guidelines" to most attendees. It's great to have a session that walks through the important stuff, along with well-constructed, more detailed material to take home.

Brad took the floor for the first half of the morning, primarily talking about naming standards and why they were used. A few highlilghts:

  • The Power of Sameness is key - do things the way that the developer would expect (this would apply to end-users of applications as well), and they are much less frustrated than if you do things the "right" way but not in the expected way. Sameness with existing standards or commonly-used components is key to this
  • Along this line, "optimize globally" - sometimes you have to make your code "less good" in order for overall global consistency and to make it easier to understand from a standards perspective
  • Be careful about overloading - overloading is great for providing "defaults", but all overloads must actually do the same thing - if methods are semantically different, use different method names
  • Defaults provided by overloads, or defaults on properties, should used the "zeroed case". This way developers can make an intelligent guess as to what the default is. This requires that parameter and property names be appropriate to the default.
  • Constructor arguments should be shortcuts for setting properties - many users think in terms of "dragging a component on" and then setting properties, so allowing a parameterless constructor with the ability to set instance properties is helpful to these developers, even though less concise

Krzysztof then walked through material geared more towards framework design overall:

  • Keep it simple - frameworks naturally become more complex and it takes explicit effort to make them simple
  • Best way to start is to write sample code the way you want developers to be able to write code against your framework - number of "new" statements in sample code is indication of complexity, since each may require that the user needs to discover a class
  • There are attributes that can be used to control debugger layout (DebuggertypeProxy - cool!) and provide hints to Intellisense - use them
  • CLS (Common Language Specification) os a cpmtract between framework designers and language designers - your framework must be CLS compliant in order to work with all .Net languages
  • Prefer abstract classes (or just base classes) to interfaces, in order to support extensibility
  • Projects to develop libraries should have an explicit API designer role
Posted by Joshua Langemann | with no comments
Filed under:

Brad offers some practical advice - a few highlights:

  • don't make your consumers "archeologists" - leave artifacts that help developers understand the intent of your object model - make intent obvious
  • Const/ReadOnly: readonly better than const if values might change
  • Namespaces: use to organize classes, ensuring that the most-used classes are the most available, hiding the more advanced classes in sub-namespaces
  • Classes:
    • ensure that they have a clear purpose - better to go overboard on classes named in a concrete fashion, rather than having too many abstract-named classes
    • think about what the developer will think the class does and whether this is obvious
    • once again, constructors should be used to capture state on instantiation - shouldn't do much work
  • Enums:
    • the default numbers assigned to enum values actually do matter since calling assemblies use the number vs. the name
    • values should be specified explicitly in a framework assembly scenario
    • flags enum names should be plural
  • Properties: use for cases when there is a "logical backing" for the property
Posted by Joshua Langemann | with no comments
Filed under:

Rocky spent some valuable time discussing n-tier and n-layer architectures.  A few memorable quotes:

"UI is expendable" - Avalon changes things again - you will have to re-write your UI to use it. Users always change their minds about UI - make it easy to change and keep business logic out of it

"Prevent code from leaking into the UI" - have, in some way, shape, or form, a Business Logic Layer that is separated from the UI

"Applications should be viewed as a set of layers" - nothing new really, but Rocky pushed distinction between tiers (implying network boundary) and layers, stipulating that layers provide code maintainability - you don't need tiers for this

And in terms of Visual Studio: "Object binding is at least as good as dataset binding - better because you have more control" - finally in .Net 2, business objects receive good UI design-time support

One attendee asked Rocky about MVC (Model/View/Controller pattern) - his answer was interesting. Rocky mentioned that MVC works for wizards and web programming, where there are specific sequences of interactions that the user needs to follow. In Windows applications, however, the user wants to have control, and a Controller tends to take the csontrol that the user should have. This jives with my limited exposure to MVC in windows applications - it tends to drive a multi-screen, somewhat modal user interface rather than the rich flexible "modeless" experience we have come to expect.

One thing I hadn't realized before was that you can inherit from a generic class with the type specified (for instance MyClass : List<int>). This is really handy in a business objects scenario where you want to deal with inherited collections.

Posted by Joshua Langemann | with no comments
Filed under:

This is cool. Over 100 desktops all running Windows Vista beta, connected to the internet, in the Windows Vista lab. I'm writing this blog entry from one of them. It is amazing the infrastructure that they have set up here - the expense and organization that they must have gone to!

Anyways, there are probably about 50 conference attendees playing around with Vista. I imagine that once the conference actually starts it will be hard to get time in the Internet Lab or the Vista areas, even with what must be thousands of connected machines in various parts of the conference center. Wow.

Posted by Joshua Langemann | 115 comment(s)
Filed under:

Rocky and Billy Hollis ran the morning part of their precon on ".Net Framework 2.0: The Smart Client Perspective". A few highlights:

They put in a strong push towards smart client applications, with processing on the client, vs. browser-based applications, in order to utilize the power of the client machine, retain more security control over data, etc.

Rocky spent some time going over new databinding features in .Net 2. Much of it I was already familiar with, but there were a few interesting comments:

- push of the new RAD development stuff as actually useful - Rocky was skeptical at first but is now convinced that Microsoft has done enough things right that the new auto-databinding functionality provides real value on real projects

- Microsoft finally produced a production-application-usable grid, the DataGridView, though there are a couple of things missing for real applications that can easily be remedied by creating your own descendant: Validation error handling on Esc or row change resets row without raising validation events on dataset.

- DataTables are now paired with DataAdapters right in the DataSet definition, with partial class support making them usable as business objects

- With partial classes, you can add interface implementations (so that you can add a level of standardization to implementations of partial classes)

- Naming standards for controls are affected by defaults that MS provides - when you drag data elements from the data sources window onto a form, the resultant controls are named according to a new naming scheme <name of property><name of control> (e.g. NameTextBox, NameLabel...) vs. traditional hungarian standard (txtName, lblName). Rocky actually uses the new standard now in his own development (tough to break old habits, he says...)

Posted by Joshua Langemann | 1 comment(s)
Filed under:
Microsoft PDC 05 is here! I'm in the middle of the first day of sessions preceding the conference, here in warm Los Angeles. I'll try and post here throughout the week with highlights from the conference, and things that I'm learning. Naturally there's a focus on .Net 2, SQL Server 2005, and Windows Vista. Should be a great week!
Posted by Joshua Langemann | with no comments
Filed under:

I received another question about NAnt, asking what Targets were, and for direction in building a custom MSI package. Targets are a fairly simple concept so I'll tackle that first. As far as building MSI's, I can offer an example of how I do it, and pointers to where you can find additional information. Here goes!

 

Targets

Targets are basically a way of grouping tasks in way that can be called from elsewhere. You can call a target from elsewhere in your NAnt file, or refer to a specific target when calling NAnt.exe. Typically a NAnt file will contain a target called "build" that contains tasks and/or calls to other targets in your build file. Here is a simplified version of a build target that I use:

 <target name="build" description="Build the application.">
  <call target="clean" />
  <call target="compile" />
  <call target="discsetup" />
  <call target="rununittests" />
  <call target="fxcop" />
 </target>

This target simply calls some other targets that contain tasks to do various things. For example, the "clean" target looks like this:

 <target name="clean" description="Remove all files from the Working directory">
  <delete>
   <fileset basedir="${working.dir}\">
    <include name="**" />
   </fileset>
  </delete>
  <mkdir dir="${working.dir}" />
 </target>

You can see that it executes a couple of tasks. First, the working directory (as in the working.dir property) is removed. Then a new working directory is created. BTW, I think that the current version of NAnt will do this just fine using the dir attribute of the delete task - feel free to improve on it!

 

Building Setup (MSI) Files

There are several ways of creating setup files through NAnt. If you use Visual Studio to configure your setup file (using a Setup Project), you can invoke Visual Studio through NAnt to build the file. If you use a third party application, you would build it in a similar fashion. You can also use a NAnt file with XML tags describing your MSI file in order to generate one, using a feature of NAntContrib.

Visual Studio

First of all, the Visual Studio method. Here's an example of how my NAnt script is set up to handle this:

 <target name="build-msi" description="Build an MSI file for installation of the program">
  <property name="msi-file-name" value="application\TellerSetup\${build.config}\TellerForPOSSE.msi" />
  <delete>
   <fileset basedir="application\TellerSetup\${build.config}">
    <include name="*" />
   </fileset>
  </delete>
  <mkdir dir="${working.dir}\log" />
  <exec failonerror="false" basedir="c:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE"
   program="devenv.exe" commandline="${build.solution} /build ${build.config} /project TellerSetup /out ${working.dir}\log\msi.log" />
  <fail if="${not file::exists(msi-file-name)}">The MSI failed to build. See log\msi.log for more details.</fail>
 </target>

First of all, I set a property called "msi-file-name" with the path to the msi file that I am going to build. This is used later on to check that the file exists.

Next, I use a delete task to remove anything that currently exists in the folder where I'll be creating the MSI. This is to clean up any files left over from the last build, so that we can start with a clean slate.

mkdir is used to create the directory that will contain the log, if it doesn't already exist. We will use this to capture a log file of the Visual Studio activity, so that we can troubleshoot it later on if needed.

Finally, the exec task is used to run Visual Studio (devenv.exe) and tell it to build the setup project. There are various command-line options that you can pass into devenv.exe. This demonstrates a few of them. Note that the output from the command is sent to a log file in our log directory.

In order to determine if the MSI was built successfully or not, I simply test to see if the file exists. Ideally I would let the exec task fail if there were problems, but I found that there were valid conditions that caused devenv.exe to set the errorlevel anyways, causing NAnt to fail the build. A fail task that raises a build error if the file doesn't exist did the trick OK - and demonstrates calling functions from expressions as well. Since Visual Studio creates the actual .msi file at the end of the build process, I figured this was pretty safe.

That should do it! Note that you will need Visual Studio installed on your build machine in order for this to work - in my case, I actually have it installed on my build server (which isn't ideal, but hasn't caused any problems for me either). This method seems to work reasonably well=

NAntContrib

NAntContrib is a project that supplements NAnt by providing a bunch of additional tasks. You can download it and access the help from http://nantcontrib.sourceforge.net/. Among other cool things, NAntContrib includes an msi task to build MSI files.

I haven't used this task and don't know how well it works. You can get the documentation for it here. I believe that there is also a tool included in the download that allows you to generate the necessary XML from a Visual Studio Setup project - looks like the slingshot task, though the documentation says that it has been deprecated, so I'm not sure what you're supposed to do now. It would be interesting if you could have NAnt generate the XML from a Setup Project and then run it using MSI, without the need for Visual Studio on the server. Feel free to post comments here if you have more information or decide to try it out.

 

You can find more information about NAnt on the main NAnt page: http://nant.sourceforge.net. There's a simple example build file at http://nant.sourceforge.net/release/latest/help/fundamentals/buildfiles.html. All the best!

Posted by Joshua Langemann | 2 comment(s)
Filed under:

Recently I ran a lab for the Denver Visual Studio User Group on open source tools NAnt, NUnit, Subversion, and Cruise Control.Net, tools that significantly improve the development cycle in .Net. I briefly touched on the NAnt feature allowing C#, VB, or J# code to be included in your NAnt file, and dynamically compiled and run by NAnt during a build. I use this feature to handle versioning of assemblies the way I want the versioning to work. One of the lab participants requested a copy of this code. As always, feel free to provide feedback!

I realize that today's version of NAnt allows an assemblyinfo.cs file to be built dynamically in the script using the <asminfo> task, providing some flexibility in how versioning and signing is handled during the build. When I put this together, either that feature didn't exist or I wasn't aware of it. Thus, my approach involves parsing assemblyinfo.cs files and substituting version numbers at build time - you may be able to improve on this. As I look at it, there are other things I could do to improve and simply it, but I'll just include the currently working copy for now, and let you adapt it to your needs.

My versioning model is based on a control file containing the last-used version number. I increment the third portion of the number during a build, and commit this change to a Subversion repository if the build was successful. This way version numbers are only assigned to successful builds, and I don't waste version numbers on builds that failed. Of course, failed builds never leave the build server.

The first target is getversion, which pulls the portions of the version number out of the version.txt file into properties:

<target name="getversion" description="Populate the version properties based on the version.txt file">
  <echo message="${nant.project.basedir}"/>
  <script language="C#">
   <code><![CDATA[
    public static void ScriptMain(Project project)
    {
     // parse version document to get version information
     string fileName = Path.Combine(project.BaseDirectory, project.Properties["build.version.filename"]);
     StreamReader reader = new StreamReader(fileName);
     string versionInfo = reader.ReadLine();
     reader.Close();
     Regex pattern = new Regex("[0-9]+");
     MatchCollection matches = pattern.Matches(versionInfo);
     if (matches.Count != 4)
      throw new Exception(string.Format("Version number {0} in {1} has incorrect format.", versionInfo, fileName));
     int major = int.Parse(matches[0].Value);
     int minor = int.Parse(matches[1].Value);
     int build = int.Parse(matches[2].Value);
     int revision = int.Parse(matches[3].Value);
     project.Properties.Add("build.version.major", major.ToString());
     project.Properties.Add("build.version.minor", minor.ToString());
     project.Properties.Add("build.version.build", build.ToString());
     project.Properties.Add("build.version.revision", revision.ToString());
    }
   ]]></code>
  </script>
  <call target="setversionstring" />
 </target>
 
 
This calls setversionstring, which simply combines the seperate properties into a single versionstring property:
 
 <target name="setversionstring" description="Set the build.version.versionstring property">
  <script language="C#">
   <code><![CDATA[
    public static void ScriptMain(Project project)
    {
     string versionString = string.Format("{0}.{1}.{2}.{3}",
      project.Properties["build.version.major"],
      project.Properties["build.version.minor"],
      project.Properties["build.version.build"],
      project.Properties["build.version.revision"]
     );
     project.Properties["build.version.versionstring"] = versionString;
     project.Log(Level.Info, versionString);
    }
   ]]></code>
  </script>
 </target>

 
The incrementbuildnumber target is called from server builds (not developer-machine builds) to increment the third section of the number:
 
 <target name="incrementbuildnumber" description="Increment the build number and write to version.txt file">
  <script language="C#">
   <code><![CDATA[
    public static void ScriptMain(Project project)
    {
     string fileName = Path.Combine(project.BaseDirectory, project.Properties["build.version.filename"]);
     int major = int.Parse(project.Properties["build.version.major"]);
     int minor = int.Parse(project.Properties["build.version.minor"]);
     int build = int.Parse(project.Properties["build.version.build"]);
     int revision = int.Parse(project.Properties["build.version.revision"]);
     build++;
     string versionString = string.Format("{0}.{1}.{2}.{3}", major, minor, build, revision);
     project.Properties["build.version.build"] = build.ToString();
     
     StreamWriter writer = new StreamWriter(fileName, false);
     writer.WriteLine(versionString);
     writer.Close();
    }
   ]]></code>
  </script>
  <call target="setversionstring" />
 </target>

 
Finally, setversion is used to update the assemblyinfo.cs files with the updated version number:
 
 <target name="setversion" description="Stamp the version info onto assemblyinfo.cs files">
  <foreach item="File" property="filename">
   <in>
    <items basedir="application">
     <include name="**\AssemblyInfo.cs"></include>
    </items>
   </in>
   <do>
    <script language="C#">
     <code><![CDATA[
     public static void ScriptMain(Project project)
     {
      //FileStream file = File.Open(project.Properties["filename"], FileMode.Open, FileAccess.ReadWrite);
      StreamReader reader = new StreamReader(project.Properties["filename"]);
      string contents = reader.ReadToEnd();
      reader.Close();
      string replacement = string.Format(
       "[assembly: AssemblyVersion(\"{0}.{1}.{2}.{3}\")]",
       project.Properties["build.version.major"],
       project.Properties["build.version.minor"],
       project.Properties["build.version.build"],
       project.Properties["build.version.revision"]
      );
      string newText = Regex.Replace(contents, @"\[assembly: AssemblyVersion\("".*""\)\]", replacement);
      StreamWriter writer = new StreamWriter(project.Properties["filename"], false);
      writer.Write(newText);
      writer.Close();
     }
     ]]>
     </code>
    </script>
   </do>
  </foreach>
 </target>
 
This way, when the project compiles, our version number will be used on each assembly. For developer builds, I hard-code the "build.version.build" to 0, so that I can identify the build as a developer build. This is accomplished with the following task, called after getversion:
 
  <property name="build.version.build" value="0" />
 
I trust that this will be helpful!
 
- Josh
Posted by Joshua Langemann | 37 comment(s)
Filed under:
More Posts Next page »