Wednesday, September 10, 2014

Mage.exe and the *.deploy extension

Building the ClickOnce manifests using mage.exe for application files with the *.deploy extension is not straightforward. Some give up and use MSBuild.exe and the GenerateDeploymentManifest task instead.

If you want to use mage.exe alone, what you need to know is that it cannot be done. At least not with mage.exe alone. Mage.exe does not have a way to build a deployment manifest (*.application file) to download *.deploy files. You must manually edit the deployment manifest (*.application file) before signing it. Specifically, you need to add the mapFileExtensions attribute to the deployment XML element.

As an example suppose you have an executable named ClickOnceText.exe in a folder named 1.0.0.0., here is what you do:

> mage -n Application -t 1.0.0.0\ClickOnceTest.exe.manifest -fd 1.0.0.0 -cf ..\code.p12 -pwd passwd
ClickOnceTest.exe.manifest successfully signed

> mv 1.0.0.0\ClickOnceTest.exe 1.0.0.0\ClickOnceTest.exe.deploy

> mage -n Deployment -t ClickOnceTest.application -appm 1.0.0.0\ClickOnceTest.exe.manifest
ClickOnceTest.application successfully created

> notepad ClickOnceTest.application

And here is the trick. Add the mapFileExtensions to the deployment element:

<deployment mapFileExtensions="true" ... >

And then sign the deployment manifest (*.application).

> mage -u ClickOnceTest.application -cf code.p12 -pwd passwd
ClickOnceTest.application successfully signed

With this information, you should be able to write a script to do your deployment with mage.exe. Of course, you will need a way to edit the deployment manifest (*.application) XML file, from your scripting language. But that would be the topic of another post.

Tuesday, January 31, 2012

LINQ to Entities, ObjectDisposedException, and Lazy Loading

Consider this code:
List<Product> redProducts = null;
using (var ctx = new AdventureWorksContext())
{
    redProducts = ctx.Products.Where(p => p.Color == "Red").ToList();
}

foreach (var p in redProducts)
{
    Console.WriteLine("{0}: {1}", p.Name, p.ProductCategory.Name);
}
At run time, I get an exception:
Unhandled Exception: System.ObjectDisposedException:
The ObjectContext instance has been disposed and
can no longer be used for operations that require
a connection.
Notice that when iterating over the redProducts, I am accessing the ProductCategory navigation property. In Entity Framework 4.0, lazy loading is enabled by default. This means that when I access the ProductCategory property, the entity tries to retrieve the ProductCategory row from the database. However, I am outside of the using block, so the entity context has already been disposed.

One solution in this case is to simply eager load all the data I need before the entity context is disposed.
redProducts = ctx.Products.Include("ProductCategory").Where(p => p.Color == "Red").ToList();
Or, I could just pull down the two pieces of data that I need:
IList redProducts = null;
using (var ctx = new AdventureWorksContext())
{
    redProducts = ctx.Products.Where(p => p.Color == "Red")
                     .Select(p => new { p.Name, Category = p.ProductCategory.Name })
                     .ToList();
}

foreach (dynamic p in redProducts)
{
    Console.WriteLine("{0}: {1}", p.Name, p.Category);
}

Tuesday, August 17, 2010

Silverlight error message: Invalid or malformed application: Check manifest

I'm a perfectionist. I like nice namespaces. I recently created a new Silverlight project and it worked well -- until I "fixed" the namespaces. I got this message:

Invalid or malformed application: Check manifest

Checking the manifest in the XAP file revealed that my EntryPointType had not been updated to reflect the new namespaces. The Silverlight project setttings were holding on to the old name. The simple fix is on the Silverlight tab, Startup object. That combo box still has the old name.

Silverlight, WCF, IIS, HTTPS, BasicHttpBinding, and UserNamePasswordValidator

Creating a WCF service and Silverlight client that work together seems like a fairly common requirement. You would probably also require username/password and HTTPS. And it would be best if your development bindings were similar to deployment bindings. Here is what I used:
  • Visual Studio 2010, .NET 4.0
  • Windows 7
  • IIS 7
Note that you cannot use the built-in ASP.NET Development Server. It does not support HTTPS. And you cannot use Message credentials without HTTPS. Here is how I did it:
  • Make sure you have IIS and the "IIS Metabase and IIS6 configuration compatibility" installed.
  • Run Visual Studio as an Administrator so that you can access IIS configuration features from within Visual Studio.
  • Create a new WCF Service Application.
    • Change the debug options on the Project Properties, Web tab to Use Local IIS Web Server.
    • Press the Create a Virtual Directory button.
    • Test that your service provides metadata. (http://localhost/WcfService1/Service1.svc?wsdl)
    • Optional: Enable directory browsing on your virtual directory.
  • Setup IIS, IE, and Visual Studio for HTTPS.
    • In Internet Information Server (IIS) Manager, select the local computer and the "Server Certificates."
    • Create a Self-Signed Certificate.
    • On the web site (not the virtual directory), add a binding for HTTPS and test that it works. (https://localhost/WcfService1/Service1.svc)
    • In Internet Explorer, install the self-signed certificate, placing the certificate in the Trusted Root Certification Authorities.
    • Change the WCF project's Project URL to use https and the machine name, instead of http and the localhost. (https://mishael/WcfService1)
    • Test that Visual Studio can launch the web service view in Internet Explorer without a certificate error.
  • Create a new Silverlight Application.
    • Add a Service Reference to the WCF service.
    • Add a button that calls the WCF service.

      int counter = 0;
      private void button1_Click(object sender, RoutedEventArgs e)
      {
          var c = new Service1Client();
          c.GetDataCompleted += c_GetDataCompleted;
          c.GetDataAsync(++counter);
      }
      
      void c_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
      {
          if (e.Error != null)
              textBlock1.Text = e.Error.Message;
          else
              textBlock1.Text = e.Result;
      }
    • Edit the generated ServiceReferences.ClientConfig to use https instead of http.
    • Edit the generated ServiceReferences.ClientConfig to use security mode of Transport.

      security mode="Transport"
  • Using the WCF Service Configuration Tool, update the WCF service as follows:
    • Create a new service, with the endpoint address pointing to the services.
    • Create a New Binding Configuration for basicHttpBinding. On the Security tab, change the mode to Transport.
    • Use the new binding configuration for the service endpoint. It will be something like this:

      
        
          
            
          
        
      
      
        
          
        
      
    • Testing the service endpoint with Internet Explorer (https://mishael/WcfService1/Service1.svc) reveals that there is an issue with multipleSiteBindingsEnabled. Comment out the multipleSiteBindingsEnabled attribute in the web.config.
  • Test that the Silverlight app can successfully communicate with the WCF service using HTTPS, including stopping at breakpoints in both the Silverlight client and WCF service.
  • Enable client username and password authentication as follows:
    • In the Web.config, switch the security from "Transport" to "TransportWithMessageCredential". and a client message credential type of "UserName".


      
         
      
    • In the ServiceReferences.ClientConfig, switch the security from "Transport" to "TransportWithMessageCredential".
    • Define a UserNamePasswordValidator class to do the username and password validation. (The base class UserNamePasswordValidator is in System.IdentifyModel.dll.)

      namespace WcfService1
      {
          public class MyUserNamePasswordValidator: UserNamePasswordValidator
          {
              public override void Validate(string userName, string password)
              {
                  if (userName == "Wally" && password == "password")
                      return;
                  throw new SecurityTokenValidationException("Unrecognized user.");
              }
          }
      }
    • Update the service behavior to use the new UserNamePasswordValidator class.

      
        
          
            
          
        
      
    • Update the Silverlight client code set a username and password.

      var c = new Service1Client();
      c.ClientCredentials.UserName.UserName = "Wally";
      c.ClientCredentials.UserName.Password = "password";
      
    • Using breakpoints, verify that the UserNamePasswordValidator class is being called.
  • In your WCF operation, throw a FaultException. Testing will reveal that on the client, all you get is a CommunicationException. Improve the availability of exception messages sent to the client as follows:
    • Update the Web.config to includeExceptionDetailInFaults.
      serviceDebug includeExceptionDetailInFaults="true"
    • Create a BehaviorExtensionElement class using the code here.
    • Update the Web.config to include this behavior extension.
      
        
          
        
      
      
    • Update the Web.config to add the extension to the endpoint behavior.
      
              
                
              
            
      
    • Update the Web.config to use this behavior for your endpoint.
      behaviorConfiguration="SilverlightFaultBehavior"

    Monday, August 16, 2010

    Setting up HTTPS for local development without installing IIS

    As the subject implies, I wanted to do some web development that used HTTPS. The WebDev.WebServer (aka Cassini) that comes with Visual Studio does not support HTTPS. I did not want to require IIS for myself or my colleagues.

    I'm not entirely sure of all the details, but this seemed to work:

    • Create a test certificate as described here. You must run as an Administrator.

      > makecert -ss My -sr LocalMachine -sky exchange -r -n "CN=Wallace Kelly" -sk WallaceKelly -pe
      
    • Get the new certificate's "thumbnail" as described here. I used the MMC snap in.
    • Configure a port with the SSL certificate, as described here. The special IP address 0.0.0.0 matches any IP address for the local machine. The certhash is the "thumbnail" without the spaces. The appid is a GUID. I used the GUID for the WebServer2, from the AssemblyInfo.cs. I'm not sure if this can be any GUID.

      > netsh http add sslcert ipport=0.0.0.0:8080 certhash=4dbcbe2656f62af17ceba1f760
      ba7d065fd919bf appid={D7A0778F-A61C-463F-8C39-893DF0AA3748}
      
      SSL Certificate successfully added
      
      
      > netsh http show sslcert
      
      SSL Certificate bindings:
      -------------------------
      
          IP:port                 : 0.0.0.0:8080
          Certificate Hash        : 4dbcbe2656f62af17ceba1f760ba7d065fd919bf
          Application ID          : {d7a0778f-a61c-463f-8c39-893df0aa3748}
          Certificate Store Name  : (null)
          Verify Client Certificate Revocation    : Enabled
          Verify Revocation Using Cached Client Certificate Only    : Disabled
          Usage Check    : Enabled
          Revocation Freshness Time : 0
          URL Retrieval Timeout   : 0
          Ctl Identifier          : (null)
          Ctl Store Name          : (null)
          DS Mapper Usage    : Disabled
          Negotiate Client Certificate    : Disabled
      
      
      
      
    • Use WebDev.WebServer2, found here.

      > WebDev.WebServer2.exe /path:"c:\temp" /prefix:"http://localhost" /port:8080
      

    Wednesday, May 19, 2010

    Custom numeric string formatting, including engineering notation

    I needed to display numbers in an engineering application. The customer wanted the numbers formatted nicely, with about four significant digits and engineering notation. .NET has very flexible number formatting. However, it lacks standard formatting into engineering notation.

    The extension point for formatting numeric strings is through the IFormatProvider and ICustomFormatter interfaces. I implemented a format provider for the engineering notation. However, exclusively using engineering notation was awkward in our application. As an alternative to exclusively using engineering notation, I decided on the following format, which mixes four significant digits, engineering notation, and thousand separators:

    3.1416E-05  31.42E-06
    0.00031416  314.2E-06
    0.0031416   0.003142
    0.031416    0.03142
    0.31416     0.3142
    3.1416      3.142
    31.416      31.42
    314.16      314.2
    3141.6      3,142
    31416       31,416
    3.1416E+05  314,159
    3.1416E+06  3.142E+06
    3.1416E+07  31.42E+06
    3.1416E+08  314.2E+06
    3.1416E+09  3.142E+09
    

    In case you want similar formatting, here is the class that implements it:

    public class EngineeringFormatProvider : IFormatProvider, ICustomFormatter
    {
        public object GetFormat(Type formatType)
        {
            return (formatType == typeof(ICustomFormatter)) ? this : null;
        }
    
        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            // for doubles, store the value of the double
            var val = Double.NaN;
            if (arg is Double)
                val = (double)arg;
    
            // for other types, try to convert to a double
            else
            {
                var typeConverter = TypeDescriptor.GetConverter(arg);
                if (typeConverter.CanConvertTo(typeof(Double)))
                {
                    try
                    {
                        val = (double)typeConverter.ConvertTo(arg, typeof(Double));
                    }
                    catch
                    {
                        // ignore
                    }
                }
    
                // if cannot convert, return a default value
                if(Double.IsNaN(val))
                    return arg == null ? String.Empty : arg.ToString();
            }
                    
            // for special cases, just write out the string
            if (val == 0.0 || Double.IsNaN(val) || Double.IsInfinity(val))
                return val.ToString();
    
            else
            {
                // calculate the exponents, as a power of 3
                var exp = Math.Log10(Math.Abs(val));
                var exp3 = (int)(Math.Floor(exp / 3.0) * 3.0);
    
                // calculate the coefficient
                var coef = val / Math.Pow(10, exp3);
    
                // special case, for example 0.3142
                if(exp3 == -3 && Math.Abs(coef / 1000.0) < 1 && Math.Abs(coef / 1000.0) > 0.1)
                    return String.Format("{0:G4}", val);
    
                // for "small" numbers
                if(exp3 <= -6)
                    return String.Format("{0:G4}E{1}{2:00}", coef, exp3 > 0 ? "+" : "", exp3);
    
                // for "large" numbers
                if(exp >= 6)
                    return String.Format("{0:G4}E{1}{2:00}", coef, exp3 > 0 ? "+" : "", exp3);
    
                // for numbers needing thousand separators
                if (exp >= 3)
                    return String.Format("{0:N0}", val);
    
                // default
                return String.Format("{0:G4}", val);
            }
        }
    }
    

    Here is how to use it:

    var p = new EngineeringFormatProvider();
    var s = String.Format(p, "{0}", number);

    Tuesday, May 18, 2010

    Closing a window or dialog box when an escape key is pressed in WPF

    When you pop up a dialog box, users expect to be able to press the ESC key to close the dialog box. It can be quite annoying otherwise. In WPF, the easiest way to do that is to set the IsCancel property of your close button like this:

    <Button Content="Close" Click="OnClose" IsCancel="True" />
    

    On a recent project, however, I did not have a close button to set IsCancel on. The solution is to use WPF commands and input gestures. Here is an example:

    <Window.CommandBindings>
       <CommandBinding Command="Close" Executed="OnCloseCmdExecuted" />
    </Window.CommandBindings>
    
    <Window.InputBindings>
       <KeyBinding Command="Close" Key="Escape" />
    </Window.InputBindings>
    

    private void OnCloseCmdExecuted(object sender, ExecutedRoutedEventArgs e)
    {
       this.Close();
    }