Archive for the ‘ASP.NET’ Category

Deploy a Windows Service on Elastic Beanstalk

I recently needed to add a Windows Service to a web app solution to handle some long-processing operations asynchronously. The web app is being deployed to Elastic Beanstalk (EB). You can log into the EC2 instance that EB creates and install the service manually, but this won’t last. EB can delete your instance at any time and create another one, and redeploy the app from it’s own git repository. So in order for the service to persist, it also has to be installed during the deployment. Fortunately for us, Amazon already supports this and there is a introductory article explaining how to do it and some sparse documentation.

Basically, you are adding a new directory called .ebextensions to your .Net project.  In that directory you place YAML files with a .config extension. The YAML files contain the commands you can execute, as described in the docs. You can have more than one file, they execute in alphabetical order. The series of commands you create allow you to install the Windows Service when deploying a web application to EB.

Unfortunately, the procedure in the article only works on the initial install. If you need to push incremental updates, the script just doesn’t work because you can’t copy files over the versions currently in use by the Windows Service. Stopping the service wasn’t working for me because the account performing the deployment didn’t seem to have enough permissions to stop the service.  But it did have enough permissions to uninstall and install the service.

The solution ended up being similar to the article, but with some variation. The correct steps for me turned out to be:

  1. Fetch the zipped files from S3, unzipping to a temp location
  2. Create the destination directory, ignoring errors (it already exists for updates)
  3. Uninstall the existing Windows Service, also ignoring errors (the service doesn’t exist on the first install)
  4. Copy the unzipped files in the temp directory to the destination directory
  5. Install the Windows Service
  6. Start the Windows Service

This translates to the following YAML file:

sources:
  “c:/service/temp”: https://s3.amazonaws.com/bucket/service/service-archive.zip
commands:
  a-create-directory:
command: mkdir c:\\service\\serviceName\\Release
    cwd: “c:/service/temp/”
    waitAfterCompletion: 0
    ignoreErrors: true
  b-remove-service
    command: C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\installutil NameOfService.exe /u > “Remove.txt”
    cwd: “c:/service/serviceNamer/Release”
    waitAfterCompletion: 11
    ignoreErrors: true
  c-copy-service
    command: copy c:\\service\\temp\\Release c:\\service\\vserviceName\\Release
    cwd: “c:/service/temp/”
    waitAfterCompletion: 0
  d-install-service
    command: C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\installutil NameOfService.exe > “Install.txt”
    cwd: “c:/service/serviceName/Release”
    waitAfterCompletion: 0
services:
  windows:
    NameOfService:
    enabled: true
    ensureRunning: true
    commands: d-install-service
The service uninstall/install commands are piping out their results to text files, so that if there is an error during execution I can RDP to the server and take a look at the specific error message. Also of note for troubleshooting, there is a directory created by the EB install process at c:\cfn. It contains a log directory, and in there is cfn-init.txt. If your install process fails, the errors can be found there.

Posted on:
Posted in .Net 4.5, ASP.NET, Cloud | Comments Off on Deploy a Windows Service on Elastic Beanstalk

Browser Stats

Here are the browser stats for my site from Jan 15 2013 to Feb 14 2013:

Browser Percent
Chrome 47.35
IE 26.41
Firefox 22.49
Safari 2.36
Opera 0.98
Android 0.41

And more detail for IE:

Version Percent
10.0 12.65
9.0 42.9
8.0 38.58
7.0 5.56
6.0 0.31

I am still seeing a lot of IE8, and more 7 than I expected.

And lastly, mobile devices:

Browser Percent
iPad 48
iPhone 26
Android 19
Blackberry 4
Posted on:
Posted in ASP.NET, Mobile | Comments Off on Browser Stats

Printing a PDF from a web page

The requirement in this case is to print a PDF document, but not allow the user to manipulate the PDF in any other way. The user should see the Print Dialog box and be able to manipulate the settings. This requirement was born out of an auditing policy where the document is audited differently for printing or viewing the PDF.  I found lots of web discussion about printing a pop-up window from the parent window, or trying to print the contents of an IFrame, but none of it worked for me. I was hit with security errors from IE about cross-domain issues, even though my pages were in the same domain, or just got a blank page. So to get a working solution, I had to step out of the web page.

The tool that really allowed me to do this is iTextSharp, a C# implementation of the iText open source java library for manipulating PDF files, and the fact that you can use JavaScript within a PDF file. I used an ASPX page which manipulates and streams the PDF file into a zero-height, zero-width IFrame to keep the user from manipulating the PDF in any way (I realize tech-savvy users could still get the PDF).  I embedded the JavaScript into the PDF, where it runs once the document is loaded and pops up the Print dialog from Adobe Reader (not the browser).

The JavaScript for printing a PDF is not so complex, I embedded it into a function:

protected string GetAutoPrintJs()
{
    var script = new StringBuilder();

    script.Append("var pp = getPrintParams();");
    script.Append("pp.interactive = pp.constants.interactionLevel.full;");
    script.Append("print(pp);");

    return script.ToString();
}

In the code-behind for the ASPX page that will stream the PDF and place the script inside the PDF file, I built this function:

protected void StreamPdf(Stream pdfSource)
{
    var outputStream = new MemoryStream();

    var pdfReader = new PdfReader(pdfSource);

    var pdfStamper = new PdfStamper(pdfReader, outputStream);

    //Add the auto-print javascript
    var writer = pdfStamper.Writer;
    writer.AddJavaScript(GetAutoPrintJs());

    pdfStamper.Close();

    var content = outputStream.ToArray();
    outputStream.Close();
    Response.ContentType = "application/pdf";
    Response.BinaryWrite(content);
    Response.End();
    outputStream.Close();
    outputStream.Dispose();
}

The PDFReader and PDFStamper  classes are members of the iTextSharp library. I used the classes together to get the stream for output and also access to PDFWriter object that allowed me to add the JavaScript.

Day of .Net Slides

I posted my slide deck from my talk (The Demise of Xcopy Deployment) at the terrific Ann Arbor Day of .Net 2010 on SlideShare. Thanks to everyone who attended both my talk and the event. A special thanks to the event organizers for doing a great job.

Debugging with WinDBG

Last week I had the opportunity to talk about debugging and WinDBG at Sogeti. We recorded the session as a Live Meeting and my colleague David Giard was kind enough post the video for me. I have a previous blog post linking to the tools I mentioned during the talk.

Why Windows Azure

As a consultant, I am always trying to see where new tools may fit so that I can help my clients. So I really need to know where the cloud fits into application development recommendations. Really, it’s just another platform that expands the options for a client. This is my 10,000 foot view of criteria for when Azure applies to solution (given what I know today):

Advantages

  • Lower cost of maintenance. You don’t own the hardware, you don’t fix it, update it, etc.
  • Pay as you go. A short term app (say for a marketing campaign) is very cheap to run.
  • The app is abstracted from the infrastructure. You don’t have access to it, so now you ignore it.
  • Scalability is easy. Apps that are seasonal or have know traffic spikes benefit from this greatly. Also apps that become more or less popular are easier to scale up or down in a very short period of time.
  • Cost. See the Azure Pricing
    • The base cost for Azure is about ~ $1000/yr just to have one active app instance ($0.12/hr). Bandwidth and disk space are extra charges on top of the base cost, but seem inexpensive to me.
    • Since bandwidth and storage are cheap in Azure, so if you have a high need for either Azure is a good idea.
  • Reliability. Azure has Service Level Agreements (three nines or better) with penalties for Microsoft if they fail.
  • There is alerting/API integration with SCOM, so if you want to monitor an Azure app with your in-house tools you can.
  • Geo-location. You can have multiple instances of your Azure app in geographically disparate locations. Of course there are issues with data sharing, authentication, etc. But it’s significantly easier with Azure than other options available today.
  • You can use Azure as a storage-only option and split your app between self hosting and Azure, or vice-versa.

Disadvantages

  • Having an app in the cloud may not be compliant with some regulatory guidelines
  • Cloud applications are web apps only. Not all solutions use web apps.
  • Identity and authentication are a challenge. The app is no longer in your domain, so you will need to look at federation or other options.
  • Most out-of-the-box apps are not hosted on Azure. Think CRM, ERP, financial, etc.
  • Apps that need access to the infrastructure aren’t going to work or need to be re-designed for the new paradigm.
  • It’s a new platform, even if it seems just like ASP.NET. This means you design differently, you develop a little differently as well. So there is a learning curve.
  • You are still responsible for data backups, I’m still not clear on how this works for the non-SQL storage options.

Overall, I think this is a significant new option for all sizes of companies. I’m not sure many companies are ready to give up the security of having mission-critical apps in-house, but I think for smaller companies Azure is a huge step forward. If you are already paying for hosting an app of some kind, give Azure a look.

Posted on:
Posted in ASP.NET, Azure, Cloud | Comments Off on Why Windows Azure

Adding A Relationship to the User Profile in Commerce Server 2009

For this post I am assuming you are familiar with Commerce Server 2009 and operation sequences, which in my opinion are the main reason to use the upgraded Commerce Server product. You can find some background on operation sequences on MSDN.

 

The problem we are trying to solve here is the addition of new properties to the Commerce Server Profile system, but we want a one-to-many relationship between the user profile and the new property. As an example, we will be adding a number of Tests to the user’s profile. In this case the user needs to participate in a number of tests that we can track, because these tests determine what products a user will be able to purchase. The more tests and test categories the user participates in, the more products they are eligible for.

 

The basic steps for this are:

  • Create the underlying SQL Server data store
  • Map the store to the user profile in Commerce Server manager
  • Create further mappings for Commerce Entities in the MetadataDefinitions.xml file
  • Create C# classes
  • Add the classes to the operation sequences in ChannelConfiguration.config

See How to Use the Profiles Schema Manager on MSDN for documentation on the Profiles editor in Commerce Server Manager.

  1. Create the underlying data table and fields in SQL Server in the profile database where the data will be stored. This is very important to do first, even though Commerce Server will let you create definitions before the underlying data store exists. (I’m assuming you are familiar with SQL Server). In my example I am going to add a number of tests per each person in the profile. The tests have and ID, name, and category.
  2. Assuming that we are adding our relationship to the User Profile, add a new field to the UserObject table that is nvarchar(255) and named after your custom profile object. Commerce Server uses this field to store the IDs (primary key) of the related items. You may need to make the field size larger if your key is large like a GUID or you expect to have many child records. 255 is the default number from the Commerce Server SDK examples.
  3. Save the database change scripts (for source control)
  4. Open Commerce Server Manager, expand the tree:
    Commerce Server Manager –> Global Resources –> Profiles –>Profile Catalog –> Data Sources –> SQL Data source (name varies) –> Data objects
  5. Create a new object: Right click the Data Objects node and choose New Data Object. Choose the database table name from the list box, the Display Name and Members will fill out automatically, even though the members are not propagated further.
  6. Add Members to your new object: For each field you want to add, right click the new node and choose New Data Member.
    1. Choose the Member Name by clicking the database field name in the list.
    2. Click the Add button.
    3. Repeat for other new members
    4. Click the Finish button.
  7. Move to the Profile Definitions node in Commerce Server Manager.
  8. Right-click the Profile Definitions node and choose New Profile Definition.
  9. Give the new profile definition a name and display name. Click the Finish button, ignoring the Custom Attributes section.
  10. Update the new node by first clicking the node and allowing the editor to load.
  11. Create a new Group by clicking the Add button and selecting Add a New Group.
    1. Name the first group “GeneralInfo” with a display name of “General Information”. This is a common Commerce Server pattern.
    2. Click the Apply button.
  12. For each property you want to add, click the Add button and choose to add a new property
  13. Give the property a name and display name. Case matters later, so pay attention to the details here.
  14. Choose the correct type
    1. If you are adding a relationship to a profile definition type:
      1. Choose Profile.
      2. Choose the definition type for the Type Reference field.
    2. If you are adding a new field
      1. Choose the appropriate data type
  15. Expand the advanced attributes node
    1. Choose the Map To Data and use the Data Source Picker to map the database field
    2. Click the Apply button, or the change won’t update the Properties panel.
  16. Be sure to define the primary key
  17. Save the Profile
  18. Now we will build the relationship between the User Object and the new Profile Item we created.
    Click the User Object node and allow the editor to load.
  19. Expand the General Information group
  20. Add a new property to the General Information group
    1. Name the property after your relationship
    2. Choose Profile as the Type
    3. In the Type Reference field navigate to your new Profile type
    4. In the Map To data field choose the database field you created in the UserObject table in step 2.
    5. Click the Apply button.
  21. Save the Profile
  22. Export the schema as an XML file (to put under source control)
  23. Now we need to map the changes we made in Commerce Server Manager into a CS2009 definition. In your web project in Visual Studio, edit MetadataDefinitions.xml
    1. Find he <CommerceEntities> node under the <DefaultChannel> node
    2. Create a new <CommerceEntity> node for the new item, Test in my example.

      <CommerceEntity name=Test>

          <DisplayName value=Test/>

          <EntityMappings>

              <EntityMapping

                csType=Microsoft.CommerceServer.Runtime.Profiles.Profile

                csAssembly=Microsoft.CommerceServer.Runtime, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

                csDefinitionName=Test

                csArea=Profiles>

                  <PropertyMappings>

                      <PropertyMapping property=TestId csProperty=GeneralInfo.TestId />

                      <PropertyMapping property=TestName csProperty=GeneralInfo.TestName />

                      <PropertyMapping property=TestCategory csProperty=GeneralInfo.TestCategory />

                  </PropertyMappings>

              </EntityMapping>

          </EntityMappings>

          <Properties>

              <Property name=TestId dataType=String />

              <Property name=TestName dataType=String />

              <Property name=TestCategory dataType=String />

          </Properties>

          <Relationships/>

      </CommerceEntity>
       

    3. Find the <CommerceEntity> node named UserProfile.
    4. Add a new PropertyMapping node in the ProperyMappings section.
    5. Add a property attribute to name the property after the name defined in step 20.
    6. Add a csProperty attribute that is the name you chose in step 20a. Case matters. The format is PropertyGroup.PropertyName (ex: GeneralInfo.Gender)
    7. Find the <relationships> node under UserProfile.
      1. Add a relationship:
        ex:

        <Relationships>

            <Relationship name=Tests type=Relationship modelName=Test isMultipleItems=true>

                <DisplayName value=Tests />

                <Description value=User’s Tests />

            </Relationship>

        </Relationships>

  24. Create the following types of classes for Commerce Server:
    1. A class to hold your new data


      namespace
      CommerceDemo

      {

          [Serializable]

          public class Test

          {

              public string TestId { get; set; }

       

              public string TestName { get; set; }

       

              public string TestCategory { get; set; }

          }

      }

    2. A processor class that inherits RelatedProfileProcessorBase

      namespace CommerceDemo

      {

          public class UserProfileTestProcessor : RelatedProfileProcessorBase

          {

              // Methods

              protected override bool CanDeleteRelatedItem(string relatedItemId)

              {

                  return true;

              }

       

              // Properties

              protected override string CommercePreferredIdKeyName

              {

                  get

                  {

                      return “GeneralInfo.Test”;

                  }

              }

       

              protected override string CommerceRelatedIdsKeyName

              {

                  get

                  {

                      return “GeneralInfo.Test”;

                  }

              }

       

              protected override string ParentProfileModelName

              {

                  get

                  {

                      return “UserProfile”;

                  }

              }

       

              protected override string PreferredItemRelationshipName

              {

                  get

                  {

                      return “TestId”;

                  }

              }

       

              protected override string ProfileModelName

              {

                  get

                  {

                      return “Test”;

                  }

              }

       

              protected override string RelationshipName

              {

                  get

                  {

                      return “Tests”;

                  }

              }

       

          }

      }

    3. A committer class that inherits ProfileCommitterBase

      namespace CommerceDemo

      {

          class UserProfileTestCommitter: ProfileCommitterBase

          {

              protected override string ProfileModelName

              {

                  get { return “Test”; }

              }

          }

      }

    4. A response builder class that inherits RelatedTestResponseBuilderBase

      namespace CommerceDemo

      {

           class TestResponseBuilder : ProfileResponseBuilderBase

          {

       

              protected override string ProfileModelName

              {

                  get

                  {

                      return “Test”;

                  }

              }

          }

      }

  25. Open ChannelConfiguration.config
    1. Find the <MessageHandler> node for “CommerceQueryOperation_UserProfile”
      1. Add <Component> nodes for the processor and response builder classes created above:

        <MessageHandler name=CommerceQueryOperation_UserProfile responseType=Microsoft.Commerce.Contracts.Messages.CommerceQueryOperationResponse, Microsoft.Commerce.Contracts, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35>

            <OperationSequence>

                <Component name=User Profile Loader type=Microsoft.Commerce.Providers.Components.UserProfileLoader, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35/>

                <Component name=User Profile Test Processor type=CommerceDemo.UserProfileTestProcessor, CommerceDemo, Version=1.0.0.0, Culture=neutral,PublicKeyToken=fbeb08efdb2d0b73/>

                <Component name=User Profile Response Builder type=Microsoft.Commerce.Providers.Components.UserProfileResponseBuilder, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35 />

                <Component name=User Profile Test Response Builder type=CommerceDemo.UserProfileTestResponseBuilder, CommerceDemo, Version=1.0.0.0, Culture=neutral,PublicKeyToken=fbeb08efdb2d0b73 />

            </OperationSequence>

        </MessageHandler>

    2. Find the <MessageHandler> node for “CommerceCreateOperation_UserProfile”
      1. Add <Component> nodes for the processor, Committer, and response builder classes created above, similar to the previous step.
    3. Find the <MessageHandler> node for “CommerceDeleteOperation_UserProfile”
      1. Add <Component> nodes for the processor, Committer, and response builder classes created above, similar to step 25a1.
  26. Create any converter classes which converts the collection of Commerce Server properties from a CommerceEntity into a typed C# object.

            public static Test ConvertTest(CommerceEntity testEntity)

            {

                Test result = new Test();

     

                result.Id = testEntity.Id;

                result.TestName = testEntity.Properties[“TestName”].ToString();

                result.TestCategory = testEntity.Properties[“TestCategory”].ToString();

     

                return result;

            }

  27. Redeploy the project
  28. Reset IIS so the Commerce Server cache is emptied.

So how does this look when wired up? That’s a complex question to be sure, but here is a simplified example, assuming we have a User object class with simple common properties:

public User GetUser(string userId)

{

    User user = null;

    OperationServiceAgent svc= new OperationServiceAgent();

 

    CommerceQuery<CommerceEntity> query = new CommerceQuery<CommerceEntity>(“UserProfile”);

    query.SearchCriteria.Model.Properties[“Email”] = userId;

 

    CommercePropertyCollection properties = new CommercePropertyCollection();

    properties.Add(“Email”);

    properties.Add(“FirstName”);

    properties.Add(“LastName”);

    query.Model.Properties = properties;

 

    CommerceQueryRelatedItem<CommerceEntity> test= new CommerceQueryRelatedItem<CommerceEntity>(“Tests”, “Test”);

    test.Model.Properties.Add(“TestId”);

    test.Model.Properties.Add(“TestNumber”);

    test.Model.Properties.Add(“TestCategory”);

    query.RelatedOperations.Add(test);

 

 

    CommerceResponse response = svc.ProcessRequest(GetRequestContext(), query.ToRequest());

   

    CommerceQueryOperationResponse  queryResponse = response.OperationResponses[0] as ommerceQueryOperationResponse;

    if (queryResponse !=null && queryResponse.CommerceEntities.Count > 0)

    {

        Convert(queryResponse.CommerceEntities[0]);

    }

    return user;

}

 

public static User Convert(CommerceEntity userEntity)

{

    User result = new User();

    result.Id = userEntity.Properties[“Id”] as string;

    result.Email = userEntity.Properties[“Email”] as string;

    result.LastName = userEntity.Properties[“FirstName”] as string;

    result.FirstName = userEntity.Properties[“LastName”] as string;

 

    CommerceRelationshipList relationships = userEntity.Properties[“Tests”] as CommerceRelationshipList;

    result.Tests = ConvertTest(relationships); //Function above in post

 

     return output;

}

Posted on:
Posted in ASP.NET, Commerce Server | Comments Off on Adding A Relationship to the User Profile in Commerce Server 2009

Useful LogParser Queries

If you deal with production servers and you don’t use LogParser, you should. It gives SQL-like abilities to query web server logs an other log types (like Event Logs). Coding Horror has a great article about the tool as well, and a link to a good article about forensic log parsing. Here are my most used web queries:

Find Pages with 500 errors
logparser “SELECT cs-uri-stem as Url, sc-status as code, COUNT(cs-uri-stem) AS Hits FROM C:\ProdLogFiles\ex*.log WHERE (sc-status >= 500) GROUP BY cs-uri-stem, code ORDER BY Hits DESC” -o:CSV >> C:\Data\ErrorPages.csv

Find 404 Requests
logparser “SELECT cs-uri-stem as Url, sc-status as code, COUNT(cs-uri-stem) AS Hits FROM C:\ProdLogFiles\ex*.log WHERE (sc-status = 404) GROUP BY cs-uri-stem, code ORDER BY Hits DESC” -o:CSV >> C:\Data\404Pages.csv

Find the Slowest Pages
logparser “SELECT TOP 100 cs-uri-stem AS Url, MIN(time-taken) as [Min], AVG(time-taken) AS [Avg], max(time-taken) AS [Max], count(time-taken) AS Hits FROM C:\ProdLogFiles\ex*.log GROUP BY Url ORDER BY [Avg] DESC” -o:CSV >> C:\Data\SlowPages.csv

Another “Why didn’t someone tell me this earlier” Post

90% of the projects I work on need to send emails, and most clients don’t allow developer SMTP servers floating around their network (for good reason). So a big headache ensues trying to get access to an Exchange or SMTP server somewhere. I have been doing this same dance for years. But today I find a blog post about sending emails without an SMTP server. The .Net calls to SMTP just write the emails out on the file system. It’s just configuration even. So much to know, so little time.

Posted on:
Posted in .Net 2.0, .Net 3.5, ASP.NET | Comments Off on Another “Why didn’t someone tell me this earlier” Post

Anti-Simplicity

Josh Holmes recently posted his presentation “The Lost Art of Simplicity”. As a consultant, I am frequently striving for simplicity for one main reason: I leave behind the code I create. Most of the time I leave the code behind to be maintained by some other developer that works for the client. So I am right there with what Josh is advocating. I wouldn’t want to be the guy trying to figure out some overly-complicated code 6 months down the line (I have been that guy before).


<RANT>


So here on my current project is anti-simplicity in action. The app is an ASP.NET app. There is a “toolbar” of sorts specific for the page. One of the toolbar “buttons” is simply a link to find more products. The “toolbar” is hosted in a user control. The developer coded this link as an ASP Linkbutton. The linkbutton has a server-side event handler to capture the click, which it then propagates outside the user control so the container control can get the click event. The hosting control then captures the event and does a redirect to a hard-coded URL. There is zero conditional logic in all the code. What happened to an HTML <A> tag? There is absolutely no reason for any server processing on this action at all! I can only fathom the reasons the developer did this:



  • There are other buttons that act that way, so all buttons should act that way
  • He’s proud of complex code and solutions
  • Because he can
  • He thinks it’s more secure, hiding the url?

In the end, the developer has forgotten why he is on the project. We are creating software for the client. This includes leaving them with something that has a lower maintenance cost by achieving simplicity.


On a related note, the UI/designers on this project are 100% anti-tables. I understand using CSS for layouts and not tables, I’m all on board with that. But using UL lists stacked next to one another to display tabular data is just wrong. Tables are part of the HTML spec for a reason, mainly tabular data. Because developers/designers perverted the use of tables in the past does not preclude their ultimate use in a solution. Zealotry is another anti-simplicity pattern in my world.


</RANT>

Posted on:
Posted in ASP.NET | Comments Off on Anti-Simplicity