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"));


    }