Monday, December 31, 2012

ClickOnce Dynamically Loaded assemblies + ninject

ClickOnce deployment of my winforms dev tool was failing to the dba team because Microsoft.Web.Administration wasn't installed. Is this feature required for the tool to be useful? Not at all. Did MEF, and it wasn't helpful at all. How do you get MEF assemblies deployed for the clients to be able to pick them up? Read about Dynamically loaded assemblies via ClickOnce. Ah the magic is happening now.
[SecurityPermission(SecurityAction.Demand, ControlAppDomain = true)]
  class DynamicDownload
  {
    // Maintain a dictionary mapping DLL names to download file groups. This is trivial for this sample, 
    // but will be important in real-world applications where a feature is spread across multiple DLLs, 
    // and you want to download all DLLs for that feature in one shot. 
    Dictionary<String, String> DllMapping = new Dictionary<String, String>();

    public DynamicDownload()
    {
      DllMapping["Domain.WebAdministration"] = "Domain.WebAdministration";
      AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
      Assembly newAssembly = null;

      if(!ApplicationDeployment.IsNetworkDeployed)
      {
        return null; //this was required to debug locally, otherwise reactive was failing to load
        throw new FileLoadException("Cannot load assemblies dynamically - application is not deployed using ClickOnce");
      }
      var deploy = ApplicationDeployment.CurrentDeployment;

      var nameParts = args.Name.Split(',');
      var dllName = nameParts[0];
      string downloadGroupName = DllMapping[dllName];
      try
      {
        deploy.DownloadFileGroup(downloadGroupName);
      }
      catch (DeploymentException de)
      {
        MessageBox.Show("Downloading file group failed. Group name: " + downloadGroupName + "; DLL name: " + args.Name);
        throw;
      }

        newAssembly = Assembly.LoadFile(Application.StartupPath + @"\" + dllName + ".dll");
      
      return newAssembly;
    }
  }
static void RegisterDynamicAssemblies()
    {
      Kernel.Bind<DynamicDownload>().ToSelf().InSingletonScope(); //this should never run twice
      var dd=Kernel.Get<DynamicDownload>(); 

      Kernel.Bind<Func<string, IAdministerIIS>>().ToMethod(context => s => new IISAdministration(s));
    }
And now that feature is dynamically loaded the first time the feature is attempted to be used.

Wednesday, November 14, 2012

Ask what that COM object is

Man this is helpful, if you are working with any COM objects. Ask it what type it is - HowTo: get the actual type name behind System.__ComObject with Visual C# or VB.NET
    public class ComHelper 
    { 
        /// <summary> 
        /// Returns a string value representing the type name of the specified COM object. 
        /// </summary> 
        /// <param name="comObj">A COM object the type name of which to return.</param> 
        /// <returns>A string containing the type name.</returns> 
        public static string GetTypeName(object comObj) 
        { 
 
            if (comObj == null) 
                return String.Empty; 
 
            if (!Marshal.IsComObject(comObj)) 
                //The specified object is not a COM object 
                return String.Empty; 
 
            IDispatch dispatch = comObj as IDispatch; 
            if (dispatch == null) 
                //The specified COM object doesn't support getting type information 
                return String.Empty; 
 
            System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo = null; 
            try 
            { 
                try 
                { 
                    // obtain the ITypeInfo interface from the object 
                    dispatch.GetTypeInfo(0, 0, out typeInfo); 
                } 
                catch (Exception ex) 
                { 
                    #if LINQPAD
                    ex.Dump();
                    #endif
                    //Cannot get the ITypeInfo interface for the specified COM object 
                    return String.Empty; 
                } 
 
                string typeName = ""; 
                string documentation, helpFile; 
                int helpContext = -1; 
 
                try 
                { 
                    //retrieves the documentation string for the specified type description 
                    typeInfo.GetDocumentation(-1, out typeName, out documentation, 
                        out helpContext, out helpFile); 
                } 
                catch (Exception ex) 
                { 
                    // Cannot extract ITypeInfo information 
                    return String.Empty; 
                } 
                return typeName; 
            } 
            catch (Exception ex) 
            { 
                // Unexpected error 
                return String.Empty; 
            } 
            finally 
            { 
                if (typeInfo != null) Marshal.ReleaseComObject(typeInfo); 
            } 
        } 
    } 
 
    /// <summary> 
    /// Exposes objects, methods and properties to programming tools and other 
    /// applications that support Automation. 
    /// </summary> 
    [ComImport()] 
    [Guid("00020400-0000-0000-C000-000000000046")] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    interface IDispatch 
    { 
        [PreserveSig] 
        int GetTypeInfoCount(out int Count); 
 
        [PreserveSig] 
        int GetTypeInfo( 
            [MarshalAs(UnmanagedType.U4)] int iTInfo, 
            [MarshalAs(UnmanagedType.U4)] int lcid, 
            out System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo); 
 
        [PreserveSig] 
        int GetIDsOfNames( 
            ref Guid riid, 
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] 
            string[] rgsNames, 
            int cNames, 
            int lcid, 
            [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId); 
 
        [PreserveSig] 
        int Invoke( 
            int dispIdMember, 
            ref Guid riid, 
            uint lcid, 
            ushort wFlags, 
            ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, 
            out object pVarResult, 
            ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo, 
            IntPtr[] pArgErr); 
    }

Automate Visual Studio from Linqpad

I really love LinqPad. Not that it doesn't have features I want, but that it just handles so many things in a convienent way. Starting with the whopping 6mb size. Enough about that. I was just sure that there was a way to reach into Visual Studio from another process and do stuff. I had forgotten how. I had forgotten that I had even succeeded before. I tried to find a way again. This is the rather short magic that was needed.
var dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");
and a simple ask/command sample in linqpad
    const string SolutionExplorerWindow="{3AE79031-E1BC-11D0-8F78-00A0C9110057}";
 const string SolutionFolder="{66A26720-8FB5-11D2-AA7E-00C04F688DDE}";
 //EnvDTE.DTE
 var dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");
 dte.Dump();
 dte.FileName.Dump();
 dte.Solution.FullName.Dump();
 dte.Windows.Item(SolutionExplorerWindow).Activate();
 //var uih=dte.ActiveWindow.Object as UIHierarchy;

Url Rewrite UI lies!

Url rewrite lies in the setup UI =( Take this pattern `piwik/piwik.php*` using wildcard matching (not regex) click Test Pattern and enter input data to test `piwik/piwik.php?anything=match` it claims `{R:0}` will then include the query string for the action properties. It lies.
When I setup a diagnostic page to see what was coming across from the rewrite, there were no query string arguments, unless the Append query string box was checked. =(

Tuesday, August 21, 2012

WcfTestClient ... bug.

Enter a absolute path, probably anything with a \ in it into the wcf test client as a request argument. and it will happily substitute what you asked it to pass with an empty nil element it seems. Wcf Test Client 11.05.50727.1 wcf test client config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IHpExstream" sendTimeout="00:05:00"
                    messageEncoding="Mtom">
                    <security mode="None" />
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://server.net/HpExstream/Exstream.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHpExstream"
                contract="IHpExstream" name="WSHttpBinding_IHpExstream" />
        </client>
    </system.serviceModel>
</configuration>

Saturday, June 23, 2012

Finding Code Issues with Roslyn

Just published my first MSDN code sample in the new gallery. It's on writing a CodeIssueProvider to extend Visual Studio for new types of code issues, and optionally provide an automated refactoring for it. The sample I used this older post which is incompatible with the latest Roslyn CTP.

Tuesday, June 12, 2012

EF complex or bulk inserts from a select statement

Compose complex inserts from a select statement.
int Insert<T>(IQueryable query,IQueryable<T> targetSet)
{
      var oQuery=(ObjectQuery)this.QueryProvider.CreateQuery(query.Expression);
        var sql=oQuery.ToTraceString();
        var propertyPositions = GetPropertyPositions(oQuery);

        var targetSql=((ObjectQuery)targetSet).ToTraceString();
        var queryParams=oQuery.Parameters.ToArray();
        System.Diagnostics.Debug.Assert(targetSql.StartsWith("SELECT"));
        var queryProperties=query.ElementType.GetProperties();
        var selectParams=sql.Substring(0,sql.IndexOf("FROM "));
        var selectAliases=Regex.Matches(selectParams,@"\sAS \[([a-zA-Z0-9_]+)\]").Cast<Match>().Select(m=>m.Groups[1].Value).ToArray();
        
        var from=targetSql.Substring(targetSql.LastIndexOf("FROM [")+("FROM [".Length-1));
        var fromAlias=from.Substring(from.LastIndexOf("AS ")+"AS ".Length);
        var target=targetSql.Substring(0,targetSql.LastIndexOf("FROM ["));
        target=target.Replace("SELECT","INSERT INTO "+from+" (")+")";
        target=target.Replace(fromAlias+".",string.Empty);
        target=Regex.Replace(target,@"\sAS \[[a-zA-z0-9]+\]",string.Empty);
        var insertParams=target.Substring(target.IndexOf('('));
        target = target.Substring(0, target.IndexOf('('));
        var names=Regex.Matches(insertParams,@"\[([a-zA-Z0-9]+)\]");
    
        var remaining=names.Cast<Match>().Select(m=>m.Groups[1].Value).Where(m=>queryProperties.Select(qp=>qp.Name).Contains(m)).ToArray(); //scrape out items that the anonymous select doesn't include a name/value for
         
          //selectAliases[propertyPositions[10]]
          //remaining[10]
        var insertParamsOrdered = remaining.Select((s, i) => new { Position = propertyPositions[i], s })
        .OrderBy(o => o.Position).Select(x => x.s).ToArray();
      var insertParamsDelimited = insertParamsOrdered.Aggregate((s1, s2) => s1 + "," + s2);
      var commandText = target + "(" + insertParamsDelimited + ")" + sql;
        var result=this.ExecuteStoreCommand(commandText,queryParams.Select(qp=>new System.Data.SqlClient.SqlParameter{ ParameterName=qp.Name, Value=qp.Value}).ToArray());
      return result;
}
With some help from http://stackoverflow.com/questions/7808607/how-does-entity-framework-manage-mapping-query-result-to-anonymous-type and http://msdn.microsoft.com/en-us/library/ee358769.aspx


Wednesday, May 23, 2012

Foray into custom model binders

I have a complex viewmodel
public class ScenarioCreationDTO
  {
    [Required]
    [StringLength(500)]
    public string Comments { get; set; }
    [Required]
    public IEnumerable<int> PriceLists { get; set; }
    public IEnumerable<PriceListCurrencyDTO> Currencies { get; set; }
    public IEnumerable<PriceListCountryDTO> Countries { get; set; }
  }
Currencies has a compound key. How do I get binding to work for either checkBoxList or RadioButtons? Sometimes the user is allowed to select multiple, other times not. Perhaps the problem was in the view?
<tbody>
                                    @foreach (SelectListItem item in ViewBag.CurrencySelect)
                                    { 
                                        <tr>
                                            <td>
                                                @if (ViewBag.MultipleCurrencies)
                                                {
                                                    @Html.RadioButton("currencies", item.Value, item.Selected)
                                                }
                                                @Html.Label(item.Text)
                                            </td>
                                        </tr>
                                    }
                                </tbody>
I did not find a RadioButtonList helper, nor a RadioButtonFor<T> that took multiple values. Thanks to Msdn magazine and Buildstarted.com I now have a custom model binder.
protected override void OnApplicationStarted()
    {
      base.OnApplicationStarted();
      ModelBinders.Binders.Add(typeof(PriceListCurrencyDTO),new PriceListCurrencyBinder());

- global.asax.cs
public class PriceListCurrencyBinder:IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      ValueProviderResult value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
      var selected = value.AttemptedValue;
      var result = Shared.SyntaxSugar.Helpers.Deserialize<PriceListCurrencyDTO>(selected);
      return result;
    }
  }

var currencySelect=currencies.Select(c => new SelectListItem()
      {
        Text = c.CurrencyCode,
        Value = Shared.SyntaxSugar.Helpers.Serialize(c),
        Selected = priceListCurrenciesSelected != null && priceListCurrenciesSelected.Any(plc=>c.PriceListID==plc.PriceListID && c.CurrencyCode==plc.CurrencyCode)
      }).ToArray();
      ViewBag.CurrencySelect = currencySelect;
[HttpPost]
    public virtual ActionResult Create(byte regionID, Int64 dealID, Model.ViewModel.Scenarios.ScenarioCreationDTO data){
//...
}
and finally the view
<tbody>
                                    @foreach (SelectListItem item in ViewBag.CurrencySelect)
                                    { var i=-1; i++;
                                        <tr>
                                            <td>
                                                @if (ViewBag.MultipleCurrencies)
                                                {
                                                    @Html.RadioButton("currencies["+i+"]", item.Value, item.Selected)
                                                }
                                                @Html.Label(item.Text)
                                            </td>
                                        </tr>
                                    }
                                </tbody>

Thursday, May 10, 2012

Clean Repository pattern in EF4


  1. The View
    1. only post the key fields and changed fields
  2. The controller action
    1. transform the post into a DTO and ensure key is set
    2. validate changed fields
  3. The domain layer
    1. accept the DTO and the list of changed properties
    2. validate all business logic
  4. The Service layer
    1. accept the DTO
    2. update ONLY the changed columns.
      1. without fetching a new copy of the object
Goal completed.

1. In the View every input has a wrapper div marked with `data-original="@Model.oldValue"`
OnClick for the save button(s): javascript marks all input fields that have changed to `disabled='disabled'`

2. In the controller action the params required are simply the key fields. I new-up a DTO, and TryUpdateModel on it. For some reason I had to manually set the key property.

3. Domain layer is a simple pass through for now, no business logic is present yet.

4. Service Layer takes in the DTO (as an interface), maps it into a concrete type, and iterates the list of changed property names, setting the ObjectStateManager to modified for each property.

1. For Instance:
 <div class="editor-label">
            @Html.LabelFor(model => model.Threshold3)
        </div>
        <div class="editor-field" data-original="@Model.Threshold3">
            @Html.EditorFor(model => model.Threshold3)
            @Html.ValidationMessageFor(model => model.Threshold3)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>


 and
<script type="text/javascript">
    $(document).ready(function () {
        $('input[type="submit"]').on('click', function () {
            $('.editor-field').each(function (index,e) {
                var input = $('input', e);
                
                var old = $(e).attr('data-original');
                if (typeof (old) !== 'undefined' && old !== false && old == $(input).val()) {
                    input.attr('disabled', 'disabled');
                }
                
            });
        });
    });
    </script>

More after the break...

Friday, May 4, 2012

Turn off custom errors mode asap

an environment is giving an error, and you need to turn off custom errors mode in the web.config.

Navigate via filesystem, open the file, find the section, edit the section, all by hand? No.

LinqPad to the rescue!

http://ideone.com/Opagg

It could stand to have the trace localonly=false feature too, but it's not there yet, feel free to try to add that feature =)

Wednesday, February 15, 2012

Recent PoC and experimentations


  • Downloaded the CruiseControl.net's Source code
    • Retargeted cctray to .net 4, added the ability for it to work against tfs2010.
  • Installed WP7 SDK
    • after the pain  I went through to allow 4 gigs of free space on my system drive (SSD), it said it only needed 515Mb to install. Yet the installer would not even attempt to install without the huge extra free space.
    • Searched for options to actually load a simple one-button app on my phone to try it out.
      • Found NOTHING that allows you to do that for free.
  • Installed Windows7 Sdk
  • Read comments from Jon Skeet indicating Eclipse is far ahead of Visual Studio and so:


    • Searched for options to write C# in Eclipse
    • Installed Eclipse, PyDev, and Jython
      • Can't get eclipse to let me using the jython interpreter so far.
    • Did a java homework assignment from a local college java class in eclipse
  • Implemented Mark Seemann's Composition root ideas in a web forms user control.
  • Took a small cursory look through a Knockout.js IDE webapp
  • Added myself and some of my personal projects to ohloh
  • Joined a minecraft protocol project  http://libminecraft.codeplex.com/

Friday, January 13, 2012

Stand up Ninject on MVC against EF4

Here's the bindings I used to get it done


    /// <summary>
    
    /// Load your modules or register your 
services here
    
    /// </summary>
    
    /// <param name="kernel">The kernel.</param>
    
private static void RegisterServices(IKernel kernel)
    {
      
  kernel.Bind<string>()
        .ToMethod(f =>
#pragma warning disable 0618 //disable obsolete warning as this is the ONLY place this method should be used in the application
          
  ProjectDb.Adapter.EfExtensions.CreateConnectionString(
#pragma warning restore 0618
          "data source=databaseSource;initial 
catalog=YourDbName;integrated 
security=True;multipleactiveresultsets=True;App=EntityFramework"))
      .Named("efString");

      
kernel.Bind(typeof(Project.Shared.Behaviors.IRepository<>))
  .To<ProjectDb.Adapter.DEVEntities>()
  .InThreadScope()
        
  .WithConstructorArgument("connectionString",kernel.Get<string>("efString"));


    }