3/10/2010
LifeCycle Solutions - Home ( the software development blog )
 

<September 2009>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Subscribe to this feed:

RSS 2.0 | Atom 1.0 |CDF






Wednesday, September 30, 2009

NopCommerce is a well-written open source eCommerce application based on .NET and SQL Server. It’s clean, full-featured, but not bulky and unwieldy like some of it’s competitors. Azure, if you aren’t familiar with it, is Microsoft’s upcoming ‘Cloud Computing’ platform, which allows customers to run .NET applications in Microsoft’s massive data centers with advanced load balancing and high availability.  One key feature of Azure, and cloud computing in general, is that the hosting can be scaled to meet demand.  If demand is high, then with literally one button click, you can make the application run on 2,3, or 20 instances (which can be thought of as servers, though they are technically virtual server instances).  You pay only for the instances and bandwidth you use at any given time.

In eCommerce, this has a very practical implications: you can ramp up the site at peak shopping times, such as holidays, and scale back when not as many people are hitting the site.  In addition, the organization need not invest in servers and IT to maintain them and can focus on their business instead of complex tasks like database backups, load balancing, and clustering infrastructure.  With Azure’s pricing model, this functionality is within the reach of mom-and-pop stores as well as larger companies.

That explains a little why NopCommerce in the cloud is cool.  But how hard is it to do?  Well, I was able to get NopCommerce running on Azure in just a few hours with relatively little fuss.  In a real project, there would of course be all of the normal issues, such as setting up products, design, and such, but Azure was really not much more difficult than your typical hosting provider.  Here’s how I went about it.

Creating a SQL Azure Database

Azure offers several different flavors of data access.  The easiest to work with is probably SQL Azure, which is essentially a slightly modified version of SQL 2008.  To set this up, you simply click ‘Create Database’ on the SQL Azure management page, and enter in a database name.  SQL Azure gives you connection strings to use in your app, with the default ADO.NET or even ODBC and OLEDB providers.  I had to adjust these connection strings (removing the ‘tcp:’ prefix).

image

Once set up, it’s possible to connect SQL Server Management Studio (after jumping through a few minor hoops), or as I did, run the SQL Azure Migration Wizard to copy a database from my PC to SQL Azure.  This tool is nice, but in the version I used had a few bugs that I think are now fixed.  A few SQL Server features aren’t supported in SQL Azure, so you have to tweak most DB scripts to run.  The wizard does this for you, but misses some. Fortunately, the errors are easy to fix:

  • ‘text’ and ‘image’ are not supported.  Use varchar(max) and varbinary(max) instead.
  • ROWGUID is not supported.  Use DEFAULT NewId()
  • Some SQL Server metadata information is not available.  This meant DotNetNuke could not function as-is against SQL Azure.

One the wizard completes, the database is deployed to the cloud, but contains no data.  I used SQL Management Studio to script the data as INSERT statements (Generate Scripts…turn off all scripts except ‘Script Data’) and run them against the SQL Azure instance.  

To test, I changed the connection string in nopCommerce’s configuration, and it worked!

Creating a New Cloud Web App

imageYou don’t simply FTP files to Azure.  Instead, they are packaged in a custom format that contains some service configuration information.  (Also, applications are not just Web apps – Worker apps, similar to windows services, can be packaged and deployed as well).  With Azure Tools for VS installed, I added a ‘Web Cloud Service’ named ‘AzureNopCommerceStore’ to the existing NopCommerce solution.

I copied the references and files from the ‘NopCommerceStore’ ran.  Somewhat to my surprise, everything built and ran, which underscores that this is the same ASP.NET you’re already used to –not a different, scary language or something like ASP.NET MVC (which, by the way is also supported in Azure).  A few bugs did arise, though.  Azure apps run in a limited trust environment in which some functionality is not permitted.  If you’ve developed in Medium Trust, you know the sorts of restrictions, but these are a even more restricted.  A list of restrictions is available here, but in NopCommerce, the mostly revolved around code that generates bitmaps and tries to save them to disk, causing a FileIOPermissions error.

This makes sense – if deployed to multiple instances, writing a file to one instance will not be ‘synced’ up to the other instances.  Instead, you are supposed to store files like this in Azure Blob Storage.  However, in interest of time, I opted to instead generate pictures on the fly, and not cache them to disk at all.  I wrote a quick page to do this, and modified ‘PictureManager’ to accommodate.

With that out of the way, the app ran with no errors (that I’ve found yet, anyway).

Publishing to the Cloud

At this point, NopCommerce is running with one foot in the cloud.  It’s running against a SQL Azure Database in Microsoft’s datacenters, but the app itself is running on a little simulated cloud on my laptop.  To get the app running in production, you right click the ‘AzureNopCommerceStore’ and click ‘publish’.  Visual Studio builds, then creates two files – a package containing all of the web pages, etc., and a configuration file.  The Azure website is opened to a page that allows uploading these files.  Once uploaded, the deployment can be run and the app is ‘live’ on Azure.

One particularly nice feature here is that the publishing infrastructure allows for a ‘Staging’ and ‘Production’ environment.  The package can be uploaded to Staging, tested, then with a single click, transferred over to Production.  Brilliant.

image 

I only get one Azure hosting service to play with in the CTP so it may go away if I decide to try something else out, but for now, you can see it running here:

http://danielazure.cloudapp.net/default.aspx

How to Build 20 Servers in 1 Click

One more cool thing I want to show here.  The promise of Azure is that the hosting service can be adjusted as needed to meet demand.  You can imagine in current scenarios if you needed to set up load balancing for an app, you would order another server, install software, deploy your app, etc. all in a few weeks at best.  If you have a virtual environment, you would provision another server in a few minutes and deploy your app, which is nice, and essentially what Azure does behind the scenes.  But check out the UI for it:

image

By changing ‘Instance count=”1”’ to “20” and clicking ‘Save’, I get 20 virtual instances serving up my site.  I did ‘2’ and it took about 5 minutes to provision.  As I understand it, future releases will support growing and shrinking instances automatically, but for now this is pretty close.

Conclusion

A few things still remain to be done.  I haven’t fully tested, especially around payment gateways and the like, so there still may be bugs to resolve.  Also, I’m not certain how SSL and custom domains work, though I imagine they are supported.  Finally, to really take advantage of Azure, it may make sense to move away from SQL Azure, which is limited to 10GB of data, to one of the other storage options.  This would require more code, but should be feasible.  For all of the hype, in my mind Azure and other cloud offerings really are just next generation hosting providers,with some services such as “infinite” storage and security federation added.  Instead of waiting hours or days for dedicated servers or shared hosting, highly available hosting is provisioned in minutes.  From a developer perspective, working with Azure required some know-how and coding, but in general it is not much more difficult than working with other hosting providers.  If you’re a developer and haven’t tried it yet, give it a shot today.

Posted by Daniel Root

Friday, July 24, 2009

imageimage

This is just a quick and pretty obvious tip, but worth posting I think.  We recently rolled out a SharePoint solution for a customer and as an afterthought I added a Wiki called 'About this Site' to the home site.  As I identified areas in training that they might need help remembering, I added pages to the wiki with very short steps to do various tasks.  In addition, I showed them how to add their own articles.  Each took just a few minutes to add, and in the end, they had easily accessible documentation on their particular SharePoint install.  I wondered if they'd use it, but I was pleasantly surprised when I logged in and saw 'Last Modified by:' wasn't me!  SharePoint can be pretty daunting to new users, and even old ones.  By adding documentation using the customer's name and terminology familiar to the customer, we made the site much more approachable.

As a bonus, I was able to export the wiki as a template and can now re-use it elsewhere, though I'll need to take care to change the customer name everywhere as needed.  I may look into putting one of these up publicly and building up a generic reusable wiki templates to install as starting places for various common SharePoint scenarios.

This idea extends to non-SharePoint apps as well.  I'm trying this concept to document some ASP.NET MVC apps for customers.  Those haven't rolled out yet, but the customer seems to like the idea so far.

Posted by Daniel Root

Tuesday, July 14, 2009

It's been a long time coming, but Azure's pricing model is now available

Windows Azure:

  • Compute @ $0.12 / hour
  • Storage @ $0.15 / GB stored
  • Storage Transactions @ $0.01 / 10K

SQL Azure:

  • Web Edition – Up to 1 GB relational database @ $9.99
  • Business Edition – Up to 10 GB relational database @ $99.99

.NET Services:

  • Messages @ $0.15/100K message operations , including Service Bus messages and Access Control tokens

Bandwidth across all three services will be charged at $0.10 in / $0.15 out / GB

This translates to a minimum of about $90 to $100 for a small single-server forms-on-data app in the cloud. This is very competitive pricing compared to Amazon E3 windows instances, which run about $1/hour and up, and comparable to low-end dedicated server hosting plans.  In addition, Microsoft is announcing initiatives to enable private cloud services via System Center and public cloud services by licensing and other efforts aimed at hosting services like MaximumASP.

Posted by Daniel Root

Monday, June 08, 2009

Frequently, I'll have add-on reports that we don't necessarily want to deploy to a full Reporting Services installation.  I don't need the scheduling or other features of Reporting Services and would rather just run them in-application and return the result.  To this end, I've written a "WebReportRender" class that takes an .rdlc and renders it as a PDF to the browser.  In WebForms, this meant some goo to render the Http headers to trigger a file download in the browser. I've wrapped all that nicely and it's served me well the past few years. Now,  ASP.NET MVC adds a nice FileContentResult which wraps some of this goo for you.  To reuse WebReportRender in  ASP.NET MVC, I had to add methods to just output the byte array without the Http Headers:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Web;
 
using Microsoft.Reporting.WebForms;
 
 
namespace LifeCycle.Reporting
{
    /// <summary>
    /// Assists in executing and rendering Reporting Service reports.
    /// </summary>
    public class WebReportRenderer : IDisposable
    {
        #region Private fields
        private string fullReportPath;
        private string downloadFileName;
        private LocalReport reportInstance;
        private MemoryStream reportMemoryStream;
        private string reportMimeType;
        #endregion
        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="WebReportRenderer"/> class.
        /// </summary>
        /// <param name="reportPath">The report path.</param>
        /// <param name="downloadFileName">Name of the download file.</param>
        public WebReportRenderer(string reportPath, string downloadFileName)
        {
            if (HttpContext.Current == null) throw new InvalidOperationException("This class is only for use from web applications.");
 
            this.downloadFileName = downloadFileName;
            fullReportPath = HttpContext.Current.Server.MapPath(reportPath);
            using (System.IO.FileStream reportFile = new System.IO.FileStream(fullReportPath, System.IO.FileMode.Open, FileAccess.Read))
            {
                reportInstance = new LocalReport();
                reportInstance.LoadReportDefinition(reportFile);
            }
        }
        #endregion
        #region Properties
        /// <summary>
        /// Gets the report instance.
        /// </summary>
        /// <value>The report instance.</value>
        public LocalReport ReportInstance
        {
            get
            {
                return reportInstance;
            }
        }
        #endregion
        #region Public Methods
        /// <summary>
        /// Renders the current ReportInstance to the user's browser as a PDF download.
        /// </summary>
        /// <param name="pdfDeviceInfoSettings">The PDF device info settings (see http://msdn2.microsoft.com/en-us/library/ms154682.aspx).</param>
        /// <returns></returns>
        public Warning[] RenderToBrowserPDF(string pdfDeviceInfoSettings)
        {
            CreateStreamCallback callback = new Microsoft.Reporting.WebForms.CreateStreamCallback(CreateWebBrowserStream);
            Warning[] warnings;
            HttpContext.Current.Response.ContentType = "application/octet-stream";
            HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + downloadFileName + "\"");
            reportInstance.Render("PDF", null, callback, out warnings);
            return warnings;
        }
        /// <summary>
        /// Renders the current ReportInstance to the user's browser as a PDF download.
        /// </summary>
        /// <returns></returns>
        public Warning[] RenderToBrowserPDF()
        {
            return RenderToBrowserPDF(null);
        }
 
        /// <summary>
        /// Renders the current ReportInstance to an email with a file attachment containing the report.
        /// </summary>
        /// <param name="pdfDeviceInfoSettings">The PDF device info settings (see http://msdn2.microsoft.com/en-us/library/ms154682.aspx).</param>
        /// <param name="toAddress">To address.</param>
        /// <param name="fromAddress">From address.</param>
        /// <param name="subject">The subject.</param>
        /// <param name="body">The body.</param>
        /// <returns></returns>
        public Warning[] RenderToEmailPDF(string pdfDeviceInfoSettings, string toAddress, string fromAddress, string subject, string body)
        {
            CreateStreamCallback callback = CreateMemoryStream;
            Warning[] warnings;
            reportInstance.Render("PDF", pdfDeviceInfoSettings, callback, out warnings);
            reportMemoryStream.Seek(0, SeekOrigin.Begin);
 
            var client = new SmtpClient();
            using (var message = new MailMessage(fromAddress, toAddress, subject, body))
            using (var attachment = new Attachment(reportMemoryStream, reportMimeType))
            {
                attachment.Name = downloadFileName;
                message.Attachments.Add(attachment);
                client.Send(message);
            }
            return warnings;
        }
 
 
        public byte[] RenderToBytesPDF()
        {
            string mimeType;
            string encoding;
            string fileNameExtension;
            string[] streams;
            Warning[] warnings;
            return reportInstance.Render("PDF", null, out mimeType, out encoding, out fileNameExtension, out streams,
                                         out warnings);
        }
 
        public byte[] RenderToBytesExcel()
        {
            string mimeType;
            string encoding;
            string fileNameExtension;
            string[] streams;
            Warning[] warnings;
            return reportInstance.Render("EXCEL", null, out mimeType, out encoding, out fileNameExtension, out streams,
                                         out warnings);
        }
 
        /// <summary>
        /// Renders the current ReportInstance to an email with a file attachment containing the report.
        /// </summary>        
        /// <param name="toAddress">To address.</param>
        /// <param name="fromAddress">From address.</param>
        /// <param name="subject">The subject.</param>
        /// <param name="body">The body.</param>
        /// <returns></returns>
        public Warning[] RenderToEmailPDF(string toAddress, string fromAddress, string subject, string body)
        {
            return RenderToEmailPDF(null, toAddress, fromAddress, subject, body);
        }
        #endregion
        #region Private Methods
        /// <summary>
        /// Formats the current response output and returns the output stream suitable for rendering to the browser as a file download.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="extension">The extension.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="mimeType">Type of the MIME.</param>
        /// <param name="willSeek">if set to <c>true</c> [will seek].</param>
        /// <returns></returns>
        private Stream CreateWebBrowserStream(string name, string extension, System.Text.Encoding encoding, string mimeType, bool willSeek)
        {
 
            return HttpContext.Current.Response.OutputStream;
        }
 
        /// <summary>
        /// Creates a memory stream that can be used by the report when rendering to an email attachment.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="extension">The extension.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="mimeType">Type of the MIME.</param>
        /// <param name="willSeek">if set to <c>true</c> [will seek].</param>
        /// <returns></returns>
        private Stream CreateMemoryStream(string name, string extension, System.Text.Encoding encoding, string mimeType, bool willSeek)
        {
            reportMemoryStream = new MemoryStream();
            reportMimeType = mimeType;
            return reportMemoryStream;
        }
        #endregion
        #region IDisposable Members
 
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.reportInstance != null) this.reportInstance.Dispose();
            if (this.reportMemoryStream != null) this.reportMemoryStream.Dispose();
        }
 
        #endregion
 
       
    }   
 
 
}

Using WebReportRenderer from ASP.NET MVC is simple:

byte[] result;
using (var renderer = new WebReportRenderer(@"~\Report.rdlc", "Report.pdf"))
{
       var adapter = new ReportTableAdapter();                
       renderer.ReportInstance.DataSources.Add(new ReportDataSource("ReportDataSet_Report", adapter.GetData()));
       ReportParameter p0 = new ReportParameter("p0", someValue);
       renderer.ReportInstance.SetParameters(new[] {p0});
       result = renderer.RenderToBytesPDF();
}
return File(result,"application/pdf", "Report.pdf");
 

This isn't something you'd use for high volume reports, or if you need advanced delivery or scheduling capabilities.  But for one-off reports that only a few people will ever use, it works pretty well.

Posted by Daniel Root

Tuesday, May 05, 2009

We've been cranking out a couple of ASP.NET MVC applications for a client in the past few weeks.   I really feel the framework has helped us achieve greater separation of concerns and testability in these line-of-business type apps.  I recently went back to a Web Forms app, and was interested that my MVC work had really helped inform that as well.  I found myself taking better care of my rendered HTML and separating logic much better.  It's been fun to work with, but there was a little learning curve, so below are a few rules of thumb I picked up along the way.  As the title suggests, these are not rules per-se, but things that can tend to help in ASP.NET MVC development.

#1 - Favor smaller controllers over larger ones  - Our first controller attempt lumped several concepts into one class and wound up with about 20 or so action methods on one class- all only tangentially related.  This made the default routes wordy and non-intuitive, and the "one big controller" difficult to debug and read.  In future projects, and when refactoring theses, we broke the app into more narrowly-scoped controllers.  For example prefer an "EmployeeVacationController" over a catch-all "EmployeeController" with action methods for Vacation and other Employee "stuff".  This rule would really apply to most custom-written classes- Keep It Simple and limit classes to a Single Responsibility!

#2 - Consider using ViewModel classes - Most MVC examples show directly using a model class, such as a LINQ-to-SQL or Entity Framework class.  The Visual Studio wiring for MVC even steers you into this concept with it's default "Add View" code-generation, which lets you quickly gen up views based on a single model class.  However, in real-world-apps you often need more than just a single table's data to build out a page.  Some examples get around this by stuffing secondary data into ViewData, but a better way to do this is to create a "roll-up" class to contain properties for _everything_ your view will need.  This has the added benefits of being more strongly-typed, supporting intellisense, being testable, and defining exactly what a view needs.  Here's an example:

public class TerminalIndexViewModel
{
    public Terminal TerminalInfo { get; set; }
 
    public IEnumerable<FuelUsage> LatestUsage { get; set; }
    public IEnumerable<FuelDelivery> LatestDeliveries { get; set; }
    public InventorySummary CurrentInventorySummary { get; set; }
}

This does mean the default code-gen isn't as useful- for example, it wouldn't gen up anything useful for an action that returned the above class.  You can get around this by creating your own code-gen in T4, or temporarily using the "main" model type (ie 'TerminalInfo') to generate the page and tweaking to to work with your ViewModel class.  This is an emerging pattern for Silverlight and WPF as well.  More on ViewModels.

#3 - Consider separating application logic into classes separate from the controller.  As an earlier post of mine demonstrates, it's easy to begin thinking of controllers as just business logic classes.  But they're not.  They are classes whose purpose is to coordinate sending and receiving model data to/from the views to business logic.  For all but the most simple forms-on-data apps, this means the controller should call out to another class to do things like query a data layer or perform calculations.  In these apps, we've had luck using a Visitor pattern.  The controller creates the visitor instance and has it "visit" the model to perform some complex business-specific calculations.  These visitors are extremely testable, since they are not at all concerned with data access or UI wiring.

#4 - Set up client-side 'conventions' using JQuery.  Instead of wiring up individual page elements in each view, establish some 'conventions' for your app's client-side behavior using a little jQuery in script referenced from your site.master.  For example this snippet will set up rules for how we want textboxes, dates, delete buttons, and messageboxes to behave accross the entire app:

$(document).ready(function() {
          $(".initialfocus").focus();
          $(".shortdate").datepicker();
          $(".longdatetime").datepicker();
          $(".print").click(function() { window.print(); });
          $(".delete").click(function() { return confirm("This record will be deleted.  Are you sure you want to continue? Click 'Ok' to delete this record or 'Cancel' to stay on this page."); });
          $(".messagebox")
              .animate({ opacity: 1.0 }, 5000)
              .fadeOut("slow"); 
      });

With the above in site.master, setting the initial focus in any view is just a matter of setting the css tag:

<input type="textbox" class="initialfocus"/>

  I blogged about this previously, and it's made the app's client-side behavior very consistent.

Posted by Daniel Root

Tuesday, March 24, 2009

image Ages ago, NDoc- a jDoc clone- was the best free .NET documentation-generating application in town.  For various reasons, that project shut down, and Microsoft released it's internal documenting system, Sandcastle, to the public.  However Sandcastle by itself is non-trivial to understand and implement.  It's designed to gen up the entire MSDN documentation site, so it can be overwhelming for creating docs for your small line-of-business app.

Enter Sandcastle Help File Builder.  This gem brings back the simplicity and open source spirit of NDoc, and adds some functionality from Sandcastle to provide a great documenting solution for small or large projects. I'd checked it out in earlier versions, but it's come a long way. It even has a console app, which can easily be integrated into your continuous integration process.   In addition, you can add documents other than the typical xml-doc and build a true compiled help that a developer may actually use.

Here's how we set ours up:

  • A solution has a _FileReferences project for file-based dll references and other non-compiled code.  (Build is false for all Build configurations).  This project contains:
    • a Help folder that  contains .htm files and the compiled .chm file
    • a .shfbproj file that manages the documentation project.
  • In the .shfbproj:
    • A site map has been added to build the table of contents.  It has nodes pointing to each .htm file.
    • The solutions' projects have been added as Documentation Sources.
  • An automated process downloads the solution from source control, checks out the .shfbproj and .chm, runs the console app to generate a new .chm, and checks everything back in.

This way, the documentation is constantly updated and available in source control alongside the project!

Posted by Daniel Root

Friday, March 20, 2009

I came across this run-time error message while trying to run a web app project from Visual Studio 2008:

"Failed to generate a user instance of SQL Server due to a failure in starting the process for the user instance. The connection will be closed."

The app has a connectionString pointing to a SQL Server 2005 Express instance, so I checked to make sure the instance service was running and it was.  As any good computer shade tree mechanic would do, I restarted the service hoping for the best, but nothing changed.

So as always, Google to the rescue.  Joe Stagner in this blog entry describes what he did to fix the problem when he experienced it.  I deleted the corresponding folder under username and it worked!  If I had more time I'd try to dig some more for an explanation on why this happens and why the fix works, but we'll leave that exercise for another day.  For now I'm just glad it works.

Standard disclaimer: make sure you have data backed up before you go deleting things willy-nilly.  This fix worked for Joe Stagner and it worked for me, but there's no guarantee it won't fubar your SQL Server Express instance.

Update: Depending on your version of Windows, this folder may not be under 'Documents and Settings', but rather: C:\Users\USERNAME\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS  Again, Your Mileage May Vary.

Posted by Daniel Root

Wednesday, March 18, 2009

Mix09 is happening this week.  This is one of Microsoft's larger developers conferences and sure to drop a few new tools, features, and plans.  Below are some cool new technologies I'm watching.  This post will be updated as I hear about interesting news, so check back from time to time.

  • Of course, as already mentioned, MVC v1 is now live!
  • Expression Web SuperPreview - effortlessly test browser compatibility in Expression Web.  I hope we get this for Visual Studio soon!
  • Silverlight 3 Beta 1 - Hardware GPU acceleration. New codecs, including AAC. Write your own codecs. IIS Media Services - static and LIVE streaming video support easily installed from Web Platform Installer (see below).  3D perspective support for any control.  Improved fonts.  Multitouch support. Support for running outside-of-browser on Windows and Mac in secure sandbox, with offline-awareness and automatic updates.  New Eclipse IDE tools for developing on Mac.  The download size - including all these new features - is actually smaller than Silverlight 2 (4.4M)!  Beta today, shipping RTM later this year.
  • Expression Blend 3 - design Silverlight 3 UIs.  Supports the new features, and has "SketchFlow" for sketching prototype page flows, transitions, and layouts, then generating documentation and actual Silverlight code.  Very cool, and also something VS needs!
  • Web Platform Installer and Web App Gallery - Make it easier to install framework and related MS features, plus popular applications that run in IIS.  They just called it "an App Store for the Web Server". Some apps included: DotNetNuke, Umbraco.
  • Azure commercial release this year.  Update this week supports PHP & CGI, has ADO.NET support that works with ADO EF, NHibernate, etc. similar to your traditional MS SQL apps.   It also supports geo-locating your applications and storage to enable edge content delivery, geo-load balancing, and other scenarios.  No pricing released yet. New SDK and Visual Studio Tools CTP available.
  • New Virtual Earth Silverlight SDK.
Posted by Daniel Root

I'm sure this will be old news in about an hour, but MVC is now live! Download here.  LifeCycle was an early adopter on this - our first production MVC app was deployed for testing at the client last week. The result was a clean separation of concerns and a light, clean standards-compliant web UI, with just enough jQuery goodness. 

Posted by Daniel Root

Friday, March 06, 2009

Need to generate create scripts for your SQL Express database?  Here's a little batch I've used several times recently to do just that:

@echo off
SET DatabaseFileName=DbName
 
ECHO Generating code for %DatabaseFileName%
ECHO Generating create script 'CreateDatabaseSchema.sql'
 
"C:\Program Files\Microsoft SQL Server\90\Tools\Publishing\SqlPubWiz.exe" script -C "Data Source=.\SQLEXPRESS;AttachDbFilename=%CD%\%DatabaseFileName%.mdf;Integrated Security=True;User Instance=True"  -noschemaqualify -schemaonly -nodropexisting -f CreateDatabaseSchema.sql

To use, just drop in a .bat next to the .mdf, change DbName and run.  It will output 'CreateDatabaseSchema.sql containing create script for all of the tables and stored procs in the database. 

Posted by Daniel Root

© 2006 LifeCycle Solutions, LLC | All Rights Reserved