Pages

Sunday, December 20, 2015

Knockout - example how to bind checkbox to radiobutton

I just want to share one small example of how to bind 2 collections together - using subscribe function. It will be done as a simple Asp.Net MVC application with Knockout + JQuery

That application solves a problem of selecting duplicates. Imagine that you have a database of customers, each customer has email address, name and id. It can later be linked to other entities like orders, but thats out of the scope of the current app. In order to manage that DB sometime you have to search for duplicates, e.g. "John Smith" with email "js@mail.com" should be the same person as "Johnny Smith" with the same email and it would be wise to let your users check that information.

So, somehow you managed to get all duplicates and want to show a form to the user to submit the merging process. I will be using that simple controller:

    public class DuplicatesController : Controller
    {
        public ActionResult Index()
        {
            var model = new DuplicatesCollectionViewModel();

            model.Duplicates = new List<DuplicateViewModel>
            {
                new DuplicateViewModel {ContactName = "Duplicate 1", ContactId = Guid.NewGuid(), Email = "mail1@mail.com", Reason = ""},
                new DuplicateViewModel {ContactName = "Duplicate 2", ContactId = Guid.NewGuid(), Email = "mail1@mail.com", Reason = "same email"}
            };

            return View(model);
        }

        public JsonResult GetAnotherDuplicate()
        {
            var number = new Random().Next(100);

            var result = new JsonResult
            {
                Data = new DuplicateViewModel
                    {
                        ContactName = string.Format("Duplicate {0}", number),
                        ContactId = Guid.NewGuid(),
                        Email = string.Format("mail{0}@mail.com", number),
                        Reason = "another one"
                    }
            };

            return result;
        }

        public void MergeContacts(List<Guid> duplicates, Guid originalId)
        {
            //do stuff
        }
    }

And model for the backend:
    public class DuplicatesCollectionViewModel
    {
        public List<DuplicateViewModel> Duplicates { get; set; }
    }

    public class DuplicateViewModel
    {
        public Guid ContactId { get; set; }

        public string ContactName { get; set; }

        public string Email { get; set; }

        public string Reason { get; set; }
    }
For the frontend I will use jquery and knockout.
There will be 2 models: DuplicateContact and DuplicatesViewModel. First one to store the information about every customer and the later one is the main model for the page. The view model has 2 observable arrays to store the all possible duplicates (from the server) and duplicates selected by user for the merge. Selected duplicates are bound to checkboxes via the "checked" binding.

Another property of the view model is the "primaryItem". It is bound to radiobuttons and also there is a subscription - to keep checkboxes and radiobuttons in sync. User can`t select primary item if it is not among merged items, so if this happens I want to include that item into the merged collection. Also that will flag item that it cant be excluded from that collection.

Well, here is the code for the page, I hope it is self-explanatory:
@model KnockoutRadiobutton.Models.DuplicatesCollectionViewModel

<div id="duplicatesForm">
    <table id="duplicatesTable">
        <thead>
        <tr>
            <th></th>
            <th></th>
            <th>Merge</th>
            <th>Primary</th>
        </tr>
        </thead>
        <tbody data-bind="foreach: duplicates">
        <tr>
            <td>
                <span style="font-weight: bold" data-bind="text: contactName"></span>
            </td>
            <td>
                <span data-bind="text: reason"></span>
            </td>
            <td>
                <input type="checkbox" data-bind="value: contactId, checked: $parent.mergeItems, attr: {disabled: isDisabled}" />
            </td>
            <td>
                <input type="radio" name="PrimaryContactRadioGroup" data-bind="value: contactId, checked: $parent.primaryItem" />
            </td>
        </tr>
        <tr>
            <td></td>
            <td style="padding-top: 0; padding-bottom: 0;">
                <span data-bind="text: email"></span>
            </td>
            <td></td>
            <td></td>
        </tr>
        </tbody>
    </table>
</div>

<div>
    <input type="button" data-bind="click: addNewDuplicate" value="Add another one" />
    <input type="button" data-bind="click: submitDuplicates" value="Send a merge request" />
</div>

<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/knockout-3.4.0.js"></script>

<script>
    function DuplicateContact(contactId, contactName, email, reason) {
        var self = this;

        self.contactId = contactId;
        self.contactName = contactName;
        self.email = email;
        self.reason = reason;

        self.isDisabled = ko.observable(false);
    }

 function DuplicatesViewModel() {
     var self = this;

  self.duplicates = ko.observableArray();
  self.mergeItems = ko.observableArray();

  self.primaryItem = ko.observable();
     self.primaryItem.subscribe(function (item) {
         //mark primary item as selected for merge
         var isForMerge = ko.utils.arrayFirst(self.mergeItems(), function(mergeItem) {
          return mergeItem === item;
      });
   if (!isForMerge) self.mergeItems.push(item);

   //mark new primary item
   ko.utils.arrayForEach(self.duplicates(), function(duplicate) {
       duplicate.isDisabled(false);
       if (duplicate.contactId === item) {
           duplicate.isDisabled(true);
       }
   });
     });

     self.getPrimaryContact = function() {
         var primaryContactId = self.primaryItem();

         var filter =  ko.utils.arrayFilter(self.duplicates(), function(duplicate) {
             return duplicate.contactId === primaryContactId;
         });


   if (filter.length === 1) return filter[0].contactId;

         return null;
  };

  self.addNewDuplicate = function() {
      $.ajax({
          type: "POST",
                url: "@Url.Action("GetAnotherDuplicate")",
                success: function(data) {
     var matchDuplicate = ko.utils.arrayFirst(self.duplicates(), function(item) {
         return data.ContactId === item.ContactId;
     });
                    if (matchDuplicate) return;

                    self.duplicates.push(new DuplicateContact(
                            data.ContactId,
                            data.ContactName,
                            data.Email,
                            data.Reason));
                }
            });
  };

     self.submitDuplicates = function () {
         var duplicates = self.mergeItems();
         var primary = self.getPrimaryContact();

         if (duplicates.length == 0) {
             alert("You have to select duplicates!");
             return;
         }
         if (!primary) {
             alert("You have to select the primary contact!");
             return;
         }

         $.ajax(
                {
                    type: "POST",
                    url: "@Url.Action("MergeContacts")",
             data: {
                 duplicates: duplicates,
                 originalId: primary,
             },
             dataType: "json",
             traditional: true
         });
     };
 }

 $(function () {
        var viewModel = new DuplicatesViewModel();

        @foreach (var duplicate in Model.Duplicates)
  {
   <text>
    viewModel.duplicates.push(new DuplicateContact(
     '@duplicate.ContactId',     
     '@duplicate.ContactName',
     '@duplicate.Email',
     '@duplicate.Reason'));
     </text>
  }

     ko.applyBindings(viewModel);
    });

// allows debugging of dynamically loaded scripts
//# sourceURL=Duplicates.js
</script>


Saturday, October 31, 2015

Presenting at the conference

Yesterday there was an in-house tech conference at stratton finance and was a part of it. Moreover - that was my first experience at presenting something in Australia!
First thing first - I really enjoyed it. And I`ve leaned a lot from the presentation itself and from preparation to it.

My topic was 'Docker containers'. I haven't used docker before so first thing I did was just a fresh installation of the docker to my Ubuntu laptop. Somehow I failed to install using official tutorial - so had to install it differently :) And when all was done I was able to pull few containers and run them - great success! Maybe, soon I`ll start using it mush more and not only locally...

We are at stratton not using docker at the moment so my presentation was a kind of 'what we can use'. Others were more about what are we using at the moment/what we will be using next week. I am not sure if this was good or not, should the theme of the conference be more focused?

Probably, the most important lesson I've learn was the obvious one - you need to prepare your speech. I was lazy and combined the presentation only the night before the event. My speech was not prepared at all - just few bullet points, almost duplicating the presentation. Surprisingly that was almost enough - I just forgot the name 'Core CLR' I wanted to use and it was an awkward pause. But other then that I hope my talk was smooth and interesting. Heh, I should ask for a feedback tomorrow!

As I said, I really enjoyed it and if the feedback would be positive enough I am going to start presenting at local meetups!


Thursday, October 22, 2015

Nullable comparisons simplified with LINQ

What if you need to compare few nullable values? For example, you store latest call date and latest email date and need to define the latest contact date - either call or email. The simplest approach in that case probably is

DateTime? callDate, emailDate;

var latestDate = callDate ?? emailDate;
if (callDate.HasValue && emailDate.HasValue)
    latestDate = callDate > emailDate
                ? callDate
                : emailDate;

Simple and straightforward. Especially if you have read Eric Lippert`s blog post about nullable comparisons. But what if you have third option - liveConversationDate? Or something even bigger? Writing all those conditions one by one can be troublesome.
I found that LINQ is the simplest approach. Just combine all values into a list, filter out nulls (or apply your business rules) and order it!

 int? a, b, c;
 a = null;
 b = 5;
 c = 3;
 
 var res = new List<int>() {a, b, c};
 Console.WriteLine(res.OrderByDescending(i => i).First());
 Console.WriteLine(res.OrderBy(i => i).First());
 Console.WriteLine(res.Where(i => i != null).OrderBy(i => i).First());

Thursday, May 28, 2015

How to compare approximate dates with SQL

Let say you have to write a code for the periodic payment application, and in your database you store the payment start date. After that you want to check if the next payment is within +/- 1 day of the current date - do something. And obviously, you font want to write a lot of code to do that :)

The first idea I had was to create a whole bunch of unions for each day and then check if current date is one of those, but later I shrinked that to 3 conditions connected with 'or' (here is NHibernate function for that)

var settlementDateDifferenceFromNow = Projections.SqlFunction(
 new SQLFunctionTemplate(
  NHibernateUtil.Int32,
  @"
  CASE WHEN
  ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()) - 1, ?1))) <= 1 OR
  ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()), ?1))) <= 1 OR
  ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()) + 1, ?1))) <= 1
  THEN 1
  ELSE 0
  END
  "),
 NHibernateUtil.Int32,
 Projections.Property<Contract>(x => x.SettlementDate_Actual)
);

I am not sure about 'or' conjunction tho... would it cause performance problems?

Monday, May 11, 2015

Nhibernate paging for time consuming operations

Imagine, you have to implement batching for a time consuming operation using NHibernate. I would write it like this:

var items = new List<Item>();
for (var page = 0; page == 0 || items.Any(); page++)
{
    items = GetListOfItems(page);
    foreach (var items in items)

    {
        DoStuff(item);
    }
}

public void GetListOfItems(int page)
{
    return CurrentSession.QueryOver<Item>()
        .Select(Projections.Id())
        .Take(PageSize)
        .Skip(PageSize * page)
        .List();
}

But what I found is that if the DuStuff is time consuming then it is possible to have duplicated items from the query. Thatis because of NHibernate generated query:

SELECT TOP (@p0) y0_
FROM (
  SELECT this_.Id AS y0_
  ,ROW_NUMBER() OVER (
    ORDER BY CURRENT_TIMESTAMP
  ) AS __hibernate_sort_row
  FROM contact.sfContact this_
) AS query
WHERE query.__hibernate_sort_row > @p1
ORDER BY query.__hibernate_sort_row;

That order by CURRENT_TIMESTAMP... If you want consistent results you would have to apply specific ordering:

public void GetListOfItems(int page)
{
    return CurrentSession.QueryOver<Item>()
        .Select(Projections.Id())
        .OrderBy(i => i.Id).Asc
        .Take(PageSize)
        .Skip(PageSize * page)
        .List();
}

Sunday, March 15, 2015

How to fail an interview (or learn from others mistakes)

I think of myself as "open for opportunities" professional. I have a great job that I like but I dont mind to try something new. Recently I found a really interesting puzzle - part of the employment process. That was a task to recreate a web service by its interface so the checking bot could access it and try its tests.

Sounds fun so I decided to try. The hardest part of that process was to understand what is the expected behavior of that service just by methods names. And take into account all tricks from the employer side (like overflow exceptions, impossible combinations of parameters etc.) I`ve solved that puzzle to 90% and that was enough to pass to the real interview. At that  point I`ve decided to try the interview as well - just in case :). Unfortunately, I failed and here is my thoughts about the interview.

That was completely my mistake. I have not really prepared myself to the interview and have not refreshed in my memory the most common questions. I thought that I dont need anything to pass.
Here is the list of some of those questions from the interview with my comments - maybe it would help someone (and all of them about c#).


  • Difference between value type and reference type 
    • hopefully everyone can explain it from the top of their heads).
  • Boxing vs Unboxing ()
    • I`ve made my first mistake here as I could not remember what is what...
  • Is string a value type or reference type?
    • Just be careful here as well as string is a special case. I`ve remembered it but not sure what I`ve answered :)
  • What is sealed keyword? Can we derive from sealed class?
  • some questions about garbage collector
    • Cant remember those questions, they were like when collector does its work by default, how to suspend it etc. I dont know a lot about it as that's not a day-to-day stuff. Possibly mine knowledge of GC.Collect and that that is not a best line of code to include just not enough :)
  • SOLID principles
    • simply name them
  • Describe Strategy pattern; Singleton pattern
    • once again, describe them in your own words
  • What are 4 types of design patterns
    • I could not remember them, but the common sense is your friend - some of them are aimed at creation of objects, other about interactions and so on.
  • Session variables in Asp.Net
    • that was a question about ViewBag, ViewData and TempData. I could not remember them (and the difference between them) as I just so get used to models. Unfortunately, I have not described my thoughts on Models either - so that is my big mistake...
  • Authorization in Asp.Net
    • I started talking about security tokens and roles, but the question was only about [Authorize(Roles = "...")] attribute. And I forgot to mention it :(
  • Can 2 different routes lead to the same action in Asp.Net?
    • Yes - but it would be better to answer with example and I just said yes.
  • Difference between Shared views and Partial Views
    • Own words explanation
  • Difference between WebForms and MVC
    • I`ve told about postbacks and stateless MVC controllers, probably that was not what interviewer was expecting...
  • Is doctype needed for HTML5?
    • I`ve said that no (from the personal experience) but accordingly to the documentation - the answer is yes...
  • What is the difference between Data Contract and Operational Contract in WCF
    • I`ve answered honestly that I dont really know WCF :( And by the way, in the job ad there was no mention of WCF
  • Can WebAPI method return ActionResult?
    • I`ve answered that yes (especially as I can wrap it as I want) but the proper answer was no
  • Can WebAPI replace WCF
    • Em... Yes?
  • Are dependencies in IoC framework singletons?
  • Lifetime management of dependencies in IoC framework
  • What will I do and how would I approach bug fixing at some client as a software development consultant?
  • How can I monitor problems in production?
  • What is TDD, BDD, DDD, Kanban?
After about hour of talking on those questions I was given a small tax calculator application with bugs and txt file with tests. My task was to fix as many bugs as possible and the interviewer was looking at my screen with Skype shared desktop. I`ve fixed some of those tests (not all of them) and after another 30 minutes interviewer asked me to show only the first test - and that was all.

And finally, on the next day or so I`ve received the email with "sorry you failed.". Not a big deal :)
Now I can learn from mine mistakes :)

Tuesday, January 20, 2015

Shortening url using google api

Today I edited a question on stackoverflow about url shortening. Looks like it was about Json serialization, and here is my solution:

 using (var client = new HttpClient())
 {
  var content = new StringContent(
   "{\"longUrl\": \"http://www.google.com/\"}",
   Encoding.UTF8,
   "application/json");

  var response = await client.PostAsync("https://www.googleapis.com/urlshortener/v1/url", content);

  var responseString = await response.Content.ReadAsStringAsync();

  var data = JsonConvert.DeserializeObject(responseString);

  var id = data.Id;
 }
And a class for urls:
 public class ShortenUrl
 {
  public string Kind { get; set; }

  public string Id { get; set; }

  public string LongUrl { get; set; }
 }
So you would have to use Newtonsoft json serializer to deserialize the json string; and HttpClient to communicate with the API