Search This Blog

Monday, December 28, 2015

Make sure you are testing/playing with regular expressions in an environment that follows the same regular expression behavior of the programming language you are using

Upon posting this question, I learn that it is mandatory, whenever testing/playing with regexes, to make sure you are using an environment that follows/matches the Regular Expression behavior of the language you are programming with.

Otherwise things will not make sense.

Check this for online sandboxes for regex testing, per programming language.

Wednesday, December 16, 2015

Nuget Helpers Package

Uploaded a nuget package containing methods I have been using very often in new projects.

Below is a list of all methods provided at this point:



Nuget install command: Install-Package Helpers

Sunday, December 13, 2015

Enforcing a given number of digits in a numeric value in Javascript

Javascript does not a built-in functionality like C#'s .ToString("0000").

To accomplish the same I came up with the following:

function setNumericFormat(value, numOfDigits, paddingChar) {
var length = value.toString().length;
if (length < numOfDigits) {
var prefix = "";
for (var i = 1; i <= numOfDigits - length; i++) {
prefix += paddingChar;
}
return prefix + value.toString();
}
return value.toString();
}

Resembles a LeftPad function...

Wednesday, November 25, 2015

Need to migrate your sql server database and wondering how will you get all the objects sql scripts ?

Found out that it is quite simple.

In case the link gets broken:

  1. Mouse right-click on the database in question, in the database explorer
  2. Select Tasks
  3. Select Generate Scripts
  4. Choose all the objects you want the scripts to be generated.
That's all.



Thursday, November 12, 2015

Project will not build after adding function or procedure to edmx

Having issues in trying to add a function/procedure to your Entity Data Model - it will not build after that ?
What's more, it works perfectly with Entity Framework version 5 ? And with 6, which you are trying now, does not ?

Well, here's the solution.

Lessons learned: there is such a thing as Entity Framework Tools for Visual Studio, and we need to make sure we have it updated/in sync with the version of Visual Studio we are using in combination with a package we have just downloaded/added to our project.

Dependencies, dependencies, dependencies...

Monday, November 9, 2015

Sunday, November 8, 2015

One more chapter on the topic "Do Not Use Regex to Parse Html"

My bit of contribution to the topic of not using regex to parse html, which comes after I put much effort in doing so :-)

See this question in stackoverflow I just posted.

Thursday, October 8, 2015

A bit about Virtual Paths in ASP.NET applications

In referring to resources such as images in your asp.net application, it is useful to know that a tilde (~) represents the root directory of the application (which you can double-check by taking a look at the framework variable HttpRuntime.AppDomainAppVirtualPath). Using it to reference resource is better since this way is not dependent of location of where the reference is being made.

 For instance, if you are referencing myImage.jpg, which resides in /Content/Images, in a view located at /Views/Partials, if you were to reference the image by its physical path you would need to manually go down to the root folder and then climb up to Content/Images. And if you copy + paste the <img src="..." />  tag where this reference was coded to another view, you might end up with a broken link, since suffices that the source file making the reference be in a different position in the hierarchy for the link to be broken.

You can get over this by having your image tag like <img src="~/Content/Images/myImage.jpg" />. .NET will always translate this path to [contents of HttpRuntime.AppDomainAppVirtualPath]/Content/Images/myImage.jpg. Using the static class VirtualPathUtility, which I just learned about, may also come in handy.

Click here for a good resource on the subject.

Monday, September 21, 2015

Generating Enums out of Database Lookup Tables

With the help of this great article, I succeeded in generating c# enumerations automatically, out of lookup key/values from a database.

 Code: (for a template include)
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>

<#+

    public static Dictionary<string, Dictionary<int, string>> GetSettings()
    {
        Dictionary<string, Dictionary<int, string>> settings = new Dictionary<string, Dictionary<int, string>>();

        try   
        {
            using (SqlConnection sqlServerConnection = new SqlConnection("my Connection String"))
            {
                sqlServerConnection.Open();

                SqlCommand sqlCommand = new SqlCommand("SELECT Group, Code, Description FROM Lookups WHERE Code = -1", sqlServerConnection);

                SqlDataReader sqlReader = sqlCommand.ExecuteReader();

                while(sqlReader.Read())
                {
                    settings.Add((sqlReader.GetString(2) ?? string.Empty).Trim(), new Dictionary<int, string>());
                }

                sqlCommand = new SqlCommand("SELECT Group, Code, Description FROM Lookups WHERE Code != -1", sqlServerConnection);

                sqlReader = sqlCommand.ExecuteReader();

                while(sqlReader.Read())
                {
                    foreach(var setting in settings)
                    {
                        setting.Value.Add(sqlReader.GetInt32(1), (sqlReader.GetString(2) ?? string.Empty).Trim());
                    }
                }

                sqlServerConnection.Close();
            }
        }

        catch (Exception)
        {
        }

        return settings;
    }
 #>



Code for the tt template:

<#@ include file="DbHelper.ttinclude" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>

namespace Lab.Generate_Enums_Dynamically

{
     <#   
        var settings = GetSettings();

        foreach (var settingGroup in settings)
        {

     #>
        public enum <#= settingGroup.Key.Replace(" ", "_") #>
        {
            <#
                List<string> enumEntries = new List<string>();
                foreach(var setting in settingGroup.Value)
                {
                    enumEntries.Add(string.Format("{0} = {1}", setting.Value.ToString().Replace(" ", "_"), setting.Key));

                }

            #>
            <#= string.Join(",\n", enumEntries) #>
        }
    <# } #>
}

Displaying Non-ASCII Characters Via Windows Command Prompt Window

I stumbled across this problem several times, but never took the time to dig in to finally find the solution. For instance, in writing a .NET Console application, if you google for it, you will find that most of the posts mention setting System.Console.OutputEncoding accordingly. But this was not making it work for me. I have figured out the problem, and the answer is developed in the following stackoverflow post.

Sunday, September 20, 2015

Getting a UserPrincipal object

I was trying relentlessly to find a way to get a UserPrincipal out of a WindowsIdentity object - because this was the only identity object I had in my application, and I was sure that whatever other identity data I would need should come from it.

Well, first of all I found out that you can create a WindowsPrincipal out of a WindowsIdentity object, just using one of WindowsPrincipal's constructors.

But most importantly, I did succeed in obtaining a UserPrincipal - but with no relation whatsoever to my Identity object.

Let me just single out the most common reason why you would be interested in a UserPrincipal object - to get a user name, out of its user string.

So, back to getting a UserPrincipal, the only pre-requisite is to have the related Domain (the Active Directory domain the user is related to) at hand. Once you have it, then all you need is:


PrincipalContext ctx = new PrincipalContext(ContextType.Domain, _domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Usf);

Tuesday, September 8, 2015

.NET Web Services Deserialization: Issues and Workarounds

I ran into the following problem:

A 3rd-party web service was given to me to be added into one .NET project I am working with.
Upon doing so, the reference.cs that is generated after adding a Service Reference into a Visual Studio project - was empty. In other words - no code was rendered that "translated" the web service definition language (wsdl) into .NET (in my case, C#) understandable code. No types to instance to make use of the service.

In a short, I researched for the topic, and found out that the Add Service Reference into a Visual Studio project action will, behind the scenes, try to deserialize the wsdl xml into C# code. In case there are problems and the process fails somehow (and a good way to know that is running the framework command tool SvcUtil.exe), a solution might be trying the deserialization in a different way.

As far as I understood (all what I am writing here is from googling, I did not check things for myself on code), the default .NET wsdl deserialization is used via the DataContractSerializer. There is a way, however, to use a different serializer the framework also offers - XmlSerializer.

To manually deserialize the wsdl into a .NET class, you can use another framework command tool: wsdl.exe.

This is what worked for me - the .cs file generated in this step was ok (not empty). I added it to my project tree and instanced the type, and was finally using the web service.

Interesting stuff on svcutil.exe can be found here.
Googling for wsdl.exe and svcutil.exe is fundamental.
For more on their difference, click here.

Thursday, September 3, 2015

Sql Server like STRAGG functionality

Making a tiny modification to this answer, you have STRAGG functionality in SqlServer:

CREATE TABLE yourtable
([FieldA] int, [FieldB] varchar(1))
;

INSERT INTO yourtable
([FieldA], [FieldB])
VALUES
(1, 'A'),
(1, 'B'),
(2, 'A')
;

select distinct t1.FieldA,
  STUFF((SELECT distinct ',' + t2.FieldB
         from yourtable t2
         where t1.FieldA = t2.FieldA
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)')
        ,1,1,'') data
from yourtable t1;

As usual, however, this ended up making me raise another question, still waiting for an answer :-)

Sunday, August 30, 2015

Extension Method to Flatten Entity Framework Validations Errors - providing Entity, Property and Error message

    public static class ExtensionMethods
    {
        public static List FlattenErrors(this DbContext dbContext)
        {
            return dbContext
                .GetValidationErrors()
                .SelectMany(e =>
                    e.ValidationErrors
                    .Select(ve =>
                        string.Format("Entity: [{0}], Property: [{1}], Error: [{2}].", ObjectContext.GetObjectType(e.Entry.Entity.GetType()).Name, ve.PropertyName, ve.ErrorMessage)
                    ))
                .ToList();
        }
    }

Wednesday, August 26, 2015

Ease debugging by serializing your objects

When you have a bunch of types containing an additional bunch of fields, it can be useful to be able to see all the values of a given object all at once, while debugging. Something that looks like:


In other words, I would like my types to be able to display all their properties in a stringified fashion (much like an object serialization). To accomplish this, I thought I could create a base class that overrides the ToString() method, and have all those classes inheriting from it - and sharing this same ToString() implementation. The same can be accomplished by adding an extension method to the object type, but the drawback would be I would have to cope with such method popping-up every time when intellisensing every object...

The snippet below illustrates how the discussed above can be achieved. The following will help only with primitive types. If your type has lists, for instance, you will need to add logic in how to treat it.

internal class Program
{
    private static void Main(string[] args)
    {
        public B()
        {
            b1 = "hello";
            b2 = "world";
        }
        B b = new B();
        Console.WriteLine(b.ToString());
        //prints hello,world
    }
}

public class A
{
    public override string ToString()
    {
        List<PropertyInfo> p = this.GetType().GetProperties().ToList();
        return string.Format(string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, p.Select(z => z.GetValue(this))));
    }
}

public class B : A
{
    public string b1 { get; set; }
    public string b2 { get; set; }
}

Thursday, August 20, 2015

Difference between filtering joins in the ON and Where clauses

Recollecting what I learned about the topic:

For Inner Joins with filters in the ON or Where clauses, the result is the same - but filtering within the ON clause restricts the row set that will be input to the next Sql engine step (where filter, here), making it more performance-wise.

The results differ in outer joins - where you filter the table you are outer joining in the ON clause.
I found a good and concise explanation here.

The great thing about this article is that it explores an example of filtering the table that is to be outer joined (i.e., all its records should be returned).

This, together with the piece of information that outer joins are logically executed after inners, means that whenever you filter within the ON clause, in such an outer join, your filter is ultimately ignored/discarded, because the outer joining will occur afterwards anyway, "bringing back" all the outer joining table records to the result set. And with no Where clause left to filter, this outer join with no where clause shows more records than the one with a where clause.

Thursday, August 13, 2015

Sql Server Management Studio T-SQL Free Formatter Plugin

Poor Man's T-SQL Formatter Plug-in

If the link gets broken, google for it.
Install the msi file and restart your SSMS at the end of installation. And that's all.
The options singled out below will be added under "Tools".


Thursday, August 6, 2015

Indispensable Developer Tools

I will keep updating this post every time I come across something new...

For the time being, here is what I know of/use:


I managed to lose the excel file where I had this typed in.
For the time being, am adding my last findings:


  • UltraSearch (file search)
  • SOAP UI
  • Instant EyeDropper (extract color code from any pixel in the screen)

Tuesday, July 21, 2015

Valuable Tools When Working With Regular Expressions

Update: This tool works well for testing Javascript's Regular Expression implementation. 
Check here for a list of tools for other implementations.

I am posting here links to some highly valuable tools I found for when you are working with regular expressions:

One is a playground/tester where you can literally play with regular expressions, in an extremely user-friendly manner.

The other is a tool for generating string samples based on a regular expression input. This is very handy when need to deal with all sorts of complex regular expressions you did not create, and you are trying to test if they are working or not. (Ex: when testing .xsd schema validations).

The playground/tester is located at http://www.regexr.com.

Features I like the most:
  1. You can hover over any part of the expression and get its meaning/understand what it does - on the fly.
  2. You can save whatever expression you have been playing with, and share them.
  3. It contains a summarized yet easy-to-understand reference for regular expression syntax components.
  4. You can type as many test strings you want, and it automatically highlights every match. It also colors every syntax component, making it easy to identify them in the pattern.
It has been really very handy. Thanks to the developer team for making it available for public use!

The samples generator resides at http://uttool.com/text/regexstr/default.aspx.

If you are interested in a C# implementation of such string samples generator, https://github.com/moodmosaic/Fare can be useful.

There is also a generator from microsoftRex

Regexplained offers a visual explanation of the regular expression. Try it!

Monday, July 20, 2015

Difference between explicit casting and using the "as" operator in C#

He who does not ask, will not learn. I would not learn this, if I did not ask - something else.
Upon raising the following question in StackOverflow, I started devising the ideal solution I would like to see for the problem (in an ideal world, a place I have been inhabiting for the past 30 years, alone).

Then, the person who offered an answer to the question - made a side remark.
Well, it then quickly had me building up a fresh new question :-)
He suggested instead of performing a cast using the "as" operator, I should do it simply with (T).

Example: say x is variable of type "object", and some type T, then I had var z = x as T;
He stated I should have var z = (T) x;

At first I noted, with the characteristic stubbornness - "it's a matter of taste. I think "as" makes the intent clearer". Minutes later, after playing with the solution, I needed to cast a double type. And thus... thankfully, I learned something new :-)
    
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(GenericCaster<string>(12345));
            Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null");
            Console.WriteLine(GenericCaster<double>(20.4));
            
            //prints:
            //12345
            //null
            //20.4

            Console.WriteLine(GenericCaster2<string>(12345));
            Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null");
            
            //will not compile -> 20.4 does not comply to the type constraint "T : class"
            //Console.WriteLine(GenericCaster2<double>(20.4));
        }

        static T GenericCaster<T>(object value, T defaultValue = default(T))
        {
            T castedValue;
            try
            {
                castedValue = (T) Convert.ChangeType(value, typeof(T));
            }
            catch (Exception)
            {
                castedValue = defaultValue;
            }

            return castedValue;
        }

        static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class
        {
            T castedValue;
            try
            {
                castedValue = Convert.ChangeType(value, typeof(T)) as T;
            }
            catch (Exception)
            {
                castedValue = defaultValue;
            }

            return castedValue;
        }
    }
 
Bottom line: GenericCaster2 will not work with struct types. GenericCaster will.

Wednesday, July 8, 2015

Accessing AppSettings in "unconventional" ways

I just learned 2 things about accessing the AppSettings node in a configuration file in a VS project:
  1. The framework provides a AppSettingsReader, not widespread in the community, that retrieves settings from the AppSettings configuration node, with some fluffing-behind-the-scenes (which I do not know what) Well, about the "fluffing"... question is what sort of fluffing is being done there, indeed... if you are wondering what's the use of a method that asks for the type of a value and nevertheless returns an object instead of a type-casted value... I raised this question here.
  2. Seems you can store app setting keys in whatever language you want. I tried storing in Hebrew and they are all retrieved without problems.
In the end, here is my version of the AppSettingsReader I think the framework should provide:

    public class Utils
    {
        AppSettingsReader _appSettingsReader;

        public Utils()
        {
            _appSettingsReader = new AppSettingsReader();
        }

        public T GetAppSettingValue<T>(string key, T defaultValue = default(T))
        {
            object value = _appSettingsReader.GetValue(key, typeof(T));
            T castedValue;
            try
            {
                castedValue = (T) Convert.ChangeType(value, typeof(T));
            }

            catch (Exception)
            {
                castedValue = defaultValue;
            }

            return castedValue;
        }

        public string GetAppSetting(string key)
        {
            return GetAppSettingValue<string>(key);
        }
    }

Monday, July 6, 2015

Software Architectures Patterns and Styles - Where is the observable pattern implemented in ASP.NET MVC implementation ?

The following material may be a bit out-dated, but I find it thoroughly essential for any developer to understand/be familiar with: Microsoft Architecture Guide.
After studying section Architectural Patterns and Styles, an interesting realization came out of it, that I believe most ASP.NET MVC developers have not thought about it (or are not aware of it):
The MVC pattern, although it relies in the Observable Pattern, in its ASP.NET implementation, does not implement any observable, publish/subscribe behaviors. This asp.net form question and answer summarize the topic.

Thursday, July 2, 2015

.NET Built-In Logging Capabilities

I just learned something nice for when you are writing some logging utilities (if you are stubborn as I am to write your own instead of using out-of-the-box frameworks such as NLog, log4net, etc). Instead of playing with exceptions trace searching for "line number" (that's what I was up to doing...) You can accomplish the same thing in a much more easy and elegant way: From MSDN:
public void DoProcessing()
{
    TraceMessage("Something happened.");
}

public void TraceMessage(string message,
        [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
        [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
        [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
    System.Diagnostics.Trace.WriteLine("message: " + message);
    System.Diagnostics.Trace.WriteLine("member name: " + memberName);
    System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
    System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}

// Sample Output: 
//  message: Something happened. 
//  member name: DoProcessing 
//  source file path: c:\Users\username\Documents\Visual Studio 2012\Projects\CallerInfoCS\CallerInfoCS\Form1.cs 
//  source line number: 31

Israeli ID Validation Attribute in ASP.NET

ID validation code extracted from Code Oasis
From this site, I learned the algorithm is based on the Luhn Algorithm
public class IsraeliIDAttribute : ValidationAttribute
    {
        public override string FormatErrorMessage(string name)
        {
            return base.FormatErrorMessage(name);
        }
 
        public override bool IsValid(object value)
        {
            string id = value as string;
 
            if (!Regex.IsMatch(id, @"^\d{5,9}$"))
                return false;
 
            // number is too short - add leading 0000
            if (id.Length < 9)
            {
                while (id.Length < 9)
                {
                    id = '0' + id;
                }
            }
 
            //validate
            int mone = 0;
            int incNum;
            for (int i = 0; i < 9; i++)
            {
                incNum = Convert.ToInt32(id[i].ToString());
                incNum *= (i % 2) + 1;
                if (incNum > 9)
                    incNum -= 9;
                mone += incNum;
            }
            if (mone % 10 == 0)
                return true;
            else
                return false;
        }
    }
Usage:
public class MyModel
{
    [IsraeliIDAttribute(ErrorMessageResourceName = "InvalidId", ErrorMessageResourceType = typeof(Validations))]
        public String ID { get; set; }
}

Thursday, June 25, 2015

Prefer Html.EditorFor over Html.TextBoxFor

From the discussion below I learned that it is preferrable (besides making the code more maintainable) to use Html.EditorFor over Html.TextBoxFor.

http://stackoverflow.com/questions/31002078/why-validation-is-not-working-with-html-textboxfor-but-is-when-using-html-editor

A more thorough coverage on the differences between EditorFor and TextBoxFor here.

Monday, May 18, 2015

Clicking a button to download a server file sent as a FileContentResult via ASP.NET Web Api

Scenario:
I have a Web Api controller that returns a .csv file as a FileContentResult.
In the UI, I have a <button> element that when clicked, I want it to trigger the file download to the local computer.

Solution:
After playing a bit with ajax calls and trying to use an anchor element instead a button to follow what many web posts suggest - which is to add "data" and "chartset" attributes plus the server uri that returns the content, like

'data:text/csv;charset=UTF-8,' + encodeURI(...)
and having no success, I then switched to a different approach - also vastly suggested on the web - to simply have the browser's window.location.href attribute set to the server uri that returns the file, like


window.location.href= encodeURI(...);
The simplest and neatest solution for this problem.

Tuesday, May 12, 2015

Http Request To Upload File(s) To The Server In ASP.NET Web Api

Instructions: if you need to attach files to a http request to upload them to a server, here's basically what you need to do:

  1. In your api post method, you need to verify that the request contains files, via Request.Content.IsMimeMultipartContent
  2. Go through the files attached to the request, using HttpContext.Request.Files, casting them to HttpPostedFileBase.
  3. Now you do what you want with the files.
To test this using a http client like google's Advanced REST Client, simply mark the request as a post method, and you will be able to click a "Files" option, like the in the image below:


Usage:
            try
            {
                if (Request.Content.IsMimeMultipartContent())
                {
                    HttpFileCollectionBase files = this.UmbracoContext.HttpContext.Request.Files;
                    foreach (String uploadedFileName in files)
                    {
                        HttpPostedFileBase httpPostedFileBase = files[uploadedFileName] as HttpPostedFileBase;

                        /* do what you want with the file, here */
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

Thursday, May 7, 2015

Use of typehead.js (autocomplete)

usage: GenerateAutoComplete(selectors.BankName, bankNamesArray); 

var GenerateAutoComplete = function (elementID, data, clear) {
    /* ------ Regular Typeahead autocomplete -------- */
    //var substringMatcher = function (strs) {
    //    return function findMatches(q, cb) {
    //        var matches, substrRegex;

    //        // an array that will be populated with substring matches
    //        matches = [];

    //        // regex used to determine if a string contains the substring `q`
    //        substrRegex = new RegExp(q, 'i');

    //        // iterate through the pool of strings and for any string that
    //        // contains the substring `q`, add it to the `matches` array
    //        $.each(strs, function (i, str) {
    //            if (substrRegex.test(str)) {
    //                // the typeahead jQuery plugin expects suggestions to a
    //                // JavaScript object, refer to typeahead docs for more info
    //                matches.push({ value: str });
    //            }
    //        });

    //        cb(matches);
    //    };
    //};

    //$(elementID).typeahead({
    //    hint: true,
    //    highlight: true,
    //    minLength: 1
    //},
    //{
    //    name: 'data',
    //    displayKey: 'value',
    //    source: substringMatcher(data)
    //});

    /*--------- Bloodhound Search Engine-----------*/
    clear = typeof clear === "undefined" ? true : clear;
    $(elementID).typeahead('destroy');

    if (clear) {
        $(elementID).val('');
    }

    // constructs the suggestion engine
    var bloodHoundData = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        local: $.map(data, function (ddlElement) { return { value: ddlElement }; }),
        limit: 10
    });

    // kicks off the loading/processing of `local` and `prefetch`
    bloodHoundData.initialize();

    $(elementID).typeahead({
        hint: true,
        highlight: true,
        minLength: 1
    },
    {
        name: 'data',
        displayKey: 'value',
        // `ttAdapter` wraps the suggestion engine in an adapter that
        // is compatible with the typeahead jQuery plugin
        source: bloodHoundData.ttAdapter()
    });
}

Categorize StackOverflow questions by tags

Follows a demo of a my "extension" to StackOverflow.
Here I can categorize all the questions I asked by tags. A click in the question will take you into the question page at StackOverflow.

Important note: this example works only as long as my local host is serving my proxy server. I have not found any .NET free web hosting services. My local host will only be up when I am using my computer, so chances are very high you will not see this working. But it does :-)

Remark: my scenario does not take into account paging in scraping the data. My questions page is one page only.

Search questions by tag:



Basically, what was done was: 
  1. Use server-side code as a proxy server, to be able to overcome the Same Origin Policy. One reason for the existence of this policy is described here.
  2. I am using .NET C#, so I made use of HtmlAgilityPack and ScrapySharp nuget packages to scrape the html from my StackOverflow questions page. There I extract all data I want to manipulate in the client, and send it back as JSON.
  3. Finally, in the client, you just do whatever you want with that data. I wanted to display an autocomplete textbox with all the tags I ever asked a question about, so this is what I have done - using typeahead.js for the autocomplete. For opening the pop-up I used jQueryUI.

I was quite happy after performing my first CORS successfully :-). I tried it a couple of times in the past, without success. Next step is doing so in the client side only, with no server code (this was my original intent, however I just came across stackoverflow material that lead me to the proxy-server solution, which is quite simple.)

The client source:

<script>
    $(document).ready(function() {
        function generateAutoComplete(elementID, data, clear) {
            /*--------- Bloodhound Search Engine-----------*/
            clear = typeof clear === "undefined" ? true : clear;
            $(elementID).typeahead('destroy');

            if (clear) {
                $(elementID).val('');
            }

            // constructs the suggestion engine
            var bloodHoundData = new Bloodhound({
                datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
                queryTokenizer: Bloodhound.tokenizers.whitespace,
                local: $.map(data, function(ddlElement) {
                    return {
                        value: ddlElement
                    };
                }),
                limit: 10
            });

            // kicks off the loading/processing of `local` and `prefetch`
            bloodHoundData.initialize();

            $(elementID).typeahead({
                hint: true,
                highlight: true,
                minLength: 1
            }, {
                name: 'data',
                displayKey: 'value',
                // `ttAdapter` wraps the suggestion engine in an adapter that
                // is compatible with the typeahead jQuery plugin
                source: bloodHoundData.ttAdapter()
            });
        }

        var serverData;
        $.ajax({
            url: 'http://localhost:56212/api/GetQuestions',
            method: 'get',
            success: function(data) {
                serverData = data;
                generateAutoComplete('#txtSearch', data.autocompleteSrc);
            },
            error: function(data) {
                console.log(data);
            },
            failed: function(data) {
                console.log(data);
            }
        });

        $("#btnShow").click(function() {
            var dialogContent = "<ul>";
            for (var i = 0; i < serverData.questionsPerTag.length; i++) {
                if (serverData.questionsPerTag[i].tag == $("#txtSearch").val()) {
                    for (var k = 0; k < serverData.questionsPerTag[i].questions.length; k++) {
                        dialogContent += "<li> <a class='link' href='" + serverData.questionsPerTag[i].links[k] + "'>" + serverData.questionsPerTag[i].questions[k] + " </a> </li><br />";
                    }
                    //dialogContent = serverData.questionsPerTag[i].questions.join();
                    break;
                }

            }
            dialogContent += "</ul>";

            $("#dialog").html(dialogContent);
            $("#dialog").dialog({
                height: 500,
                width: 1000
            });

        });

        $("#dialog").on("click", "li .link", function(event) {
            window.open(event.currentTarget.getAttribute("href"), "_blank");
        });

        $(".tt-dataset.tt-dataset-data").css("color", "000");
    });
</script>

Server-side code:
  public class ScrapingController : ApiController
    {
        [AllowAnonymous]
        [HttpGet]
        [Route("api/GetQuestions")]
        public dynamic GetQuestions()
        {

            //HttpContext.Current.Response.Headers.Add("Access-Control-Allow-Origin", "http://devrecipeshb.blogspot.co.il/*");
            HttpContext.Current.Response.Headers.Add("Access-Control-Allow-Origin", "*");
            HttpClient httpClient = new HttpClient();
            String result = httpClient.GetStringAsync("http://stackoverflow.com/users/1219280/veverke?tab=questions").Result;

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(result);
            HtmlNode divUserQuestions = doc.GetElementbyId("user-tab-questions");

            List questionsSummary = divUserQuestions.CssSelect(".question-summary .summary").ToList();
            List originalData = new List();

            foreach (HtmlNode questionSummary in questionsSummary)
            {
                originalData.Add(new SOQuestion
                {
                    Text = questionSummary.CssSelect("h3").FirstOrDefault().InnerText,
                    Link = questionsSummary.CssSelect(".question-hyperlink").FirstOrDefault().GetAttributeValue("href"),
                    Tags = ExtractTags(questionSummary.CssSelect(".tags").FirstOrDefault())
                });
            }

            List tags = new List(originalData.SelectMany(q => q.Tags).Distinct());
            List questionsPerTagResult = new List();

            foreach(string tag in tags)
            {
                SOQuestionsPerTag questionsPerTag = new SOQuestionsPerTag();

                questionsPerTag.Tag = tag;
                foreach(SOQuestion question in originalData)
                {
                    if (question.Tags != null && question.Tags.Contains(tag))
                    {
                        questionsPerTag.Questions.Add(question.Text);
                        questionsPerTag.Links.Add("http://www.stackoverflow.com/" + question.Link);
                    }
                }

                questionsPerTagResult.Add(questionsPerTag);
            }

            return new { autocompleteSrc = tags, questionsPerTag = questionsPerTagResult };

        }

        private List ExtractTags(HtmlNode tagsDiv)
        {
            return tagsDiv.InnerText.Trim().Split().ToList();
        }
    }

Wednesday, May 6, 2015

Localization in MVC .NET using C#

Refer to this post of mine at stackoverflow.

I spent hours trying to understand why my set up was not working, to figure out that each language that is found in the header MUST have a matching .resx file named [YouResourceFile].[language-2-digit-code].resx, otherwise localizing to that language will use the default resource, since the matching resource was not found.

Tuesday, May 5, 2015

Aggreagating rows in oracle per group using LISTAGG (version 11 and on)

protected T GetExistingImprovementFullData(string policyOrAccountNo, bool isLifeTrack, string usf, int taskNumber, string taskSpecificTableName, bool discardAlreadyApproved = false) where T : OraBaseImprovement, new()
        {
            T completeExistingImprovementData = new T();

            string query = string.Format(@"
                        SELECT *
                        FROM
                           (SELECT imp.improvement_id,
                                   imp.record_status,
                                   imp.user_comments,
                                   imp.usf_number,
                                   imp.task_number,
                                   imp.policy_number,
                                   imp.provident_fund_account_number,
                                   imp.improver_status,
                                   imp.improver_sub_status,
                                   imp.manager_batchly_approved,
                                   imp.manager_manually_approved,
                                   imp.manager_approval_date,
                                   imp.usf_manager,
                                   imp.create_date,
                                   imp.update_date,
                                   imp.tv08_status,
                                   LISTAGG('[' || ADAdocs.document_id        || ','
                                               || TRIM(ADAdocs.Doc_Type)|| ','
                                               || TO_CHAR(ADAdocs.Attachment_Date, 'dd/mm/yyyy hh24:mi:ss')    || ']',
                                           ',')
                            WITHIN
                             GROUP(ORDER BY impDocs.Document_Id) as documents
                            FROM pia_improvements imp
                            LEFT JOIN pia_improvement_attachments impDocs ON impDocs.improvement_id = imp.improvement_id
                            LEFT JOIN pia_ada_attached_documents ADAdocs ON ADAdocs.document_id = impDocs.document_id
                            WHERE {0} = '{1}'
                            AND imp.usf_number = '{2}'
                            AND imp.task_number = {3}
                            AND imp.record_status = 1   /* improvement is active */
                            {4}
                            GROUP BY imp.improvement_id,
                                     imp.record_status,
                                     imp.user_comments,
                                     imp.usf_number,
                                     imp.task_number,
                                     imp.policy_number,
                                     imp.provident_fund_account_number,
                                     imp.improver_status,
                                     imp.improver_sub_status,
                                     imp.manager_batchly_approved,
                                     imp.manager_manually_approved,
                                     imp.manager_approval_date,
                                     imp.usf_manager,
                                     imp.create_date,
                                     imp.update_date,
                                     imp.tv08_status ) baseImp
                            JOIN {5} task ON task.improvement_id = baseImp.improvement_id ",
                    DetermineWhereColumnNameBasedOnTrack(isLifeTrack),
                    policyOrAccountNo,
                    usf,
                    taskNumber,
                    discardAlreadyApproved ? GetDiscardImprovementAlreadyApprovedSQL() : string.Empty,
                    taskSpecificTableName);

            DataTable improvementDataTable = ExecuteDataTable(query);

            if (improvementDataTable.Rows.Count > 0)
            {
                completeExistingImprovementData = OracleMapper.MapDataRowToModel(improvementDataTable.Rows[0]);

                if (!string.IsNullOrEmpty(completeExistingImprovementData.USER_COMMENTS))
                {
                    completeExistingImprovementData.USER_COMMENTS = HebUtils.Ascii7BitToUnicode(completeExistingImprovementData.USER_COMMENTS);
                }
            }

            return completeExistingImprovementData;
        }

REST Service reader using HttpWebRequest and HttpWebResponse (now deprecated, prefer using HttpClient)

Instructions: The key component here is Newtonsoft JSON library, specifically the SelectToken method. Use it to specify the path from where you want to start extracting your data from the json object the service serves.

Usage:
public class RESTServiceBL  
{  
     public Response<List<TServiceModel>> CallRESTService<TServiceModel>(RESTServiceRequest request)  
     {  
       return Integrator.GetData<TServiceModel>(request, Enums.DataRequestTypes.RESTService);  
     }  
}  
 using Fnx.Ambulatory.Data.Sql.Actions;  
 using Fnx.Ambulatory.Data.Sql.Contexts;  
 using Fnx.Ambulatory.Entities.Domain.DataEntities;  
 using Fnx.Ambulatory.Entities.Transfer.Request;  
 using Fnx.Ambulatory.Entities.Transfer.Response;  
 using Fnx.Ambulatory.Infrastructure.Common;  
 using Fnx.Ambulatory.Infrastructure.Common.Types;  
 using Newtonsoft;  
 using Newtonsoft.Json;  
 using Newtonsoft.Json.Linq;  
 using Phoenix.CommonUtils;  
 using System;  
 using System.Collections;  
 using System.Collections.Generic;  
 using System.Collections.ObjectModel;  
 using System.Data;  
 using System.Data.Entity;  
 using System.IO;  
 using System.Linq;  
 using System.Net;  
 using System.Reflection;  
 using System.Text;  
 namespace Fnx.Ambulatory.Data.Integrator  
 {  
   public static class Integrator  
   {  
     public static Response<List<T>> GetData<T>(object parameter, Enums.DataRequestTypes requestType)  
     {  
       Response<List<T>> response = new Response<List<T>>();  
       switch (requestType)  
       {  
         case Enums.DataRequestTypes.Database:  
           Enums.DatabaseDataRequestTypes databaseRequestType;  
           AmbulatoryRequestActions ambulatoryRequestAction = new AmbulatoryRequestActions();  
           Enum.TryParse<Enums.DatabaseDataRequestTypes>(parameter.ToString(), out databaseRequestType);  
           switch (databaseRequestType)  
           {  
             //TODO - all the implementation for obtaining data from the DB...  
             case Enums.DatabaseDataRequestTypes.GetTable:  
               AmbulatoryBaseContext db = new AmbulatoryBaseContext();  
               Type type = typeof(AmbulatoryBaseContext);  
               List<PropertyInfo> properties = type.GetProperties(BindingFlags.Public).ToList();  
               Type t = properties.Find(property => property.PropertyType.Name == typeof(T).Name).PropertyType;  
               //    MethodInfo method = t.GetMethod("GenericMethod");  
               //    MethodInfo generic = method.MakeGenericMethod(myType);  
               //    generic.Invoke(this, null);  
               //    data = db.AmbulatoryRequests.ToList<type>();  
               //    //data = //dbContext as IEnumerable<AmbulatoryRequest>;  
               break;  
           }  
           //response.Data = data as List<AmbulatoryRequest>;  
           break;  
         case Enums.DataRequestTypes.RESTService:  
           response = CallRESTService<T>(parameter as RESTServiceRequest);  
           break;  
         case Enums.DataRequestTypes.SOAPService:  
           break;  
       }  
       return response;  
     }  
     private static Response<List<TServiceModel>> CallRESTService<TServiceModel>(RESTServiceRequest request)  
     {  
       Response<object> response = new Response<object> { Data = string.Empty };  
       List<TServiceModel> modelList = new List<TServiceModel>();  
       if (!string.IsNullOrEmpty(request.URI))  
       {  
         Logger.Info(string.Format("About to call REST Service... Service URI: [{0}].", request.URI));  
         response = GetRESTServiceResponse(request.URI);  
         if (response.Status == Enums.ResponseStatus.Success)  
         {  
           try  
           {  
             JArray entries = JToken.Parse(response.Data.ToString()).SelectToken(request.CollectionPath) as JArray;  
             string modelJsonStr = string.Empty;  
             //if no Entry element was found, skip...  
             if (entries != null)  
             {  
               foreach (JToken entry in entries)  
               {  
                 modelList.Add(JToken.Parse(CustomizeEntryString(entry, request.ViewModelPath, request.ReplaceStrings)).ToObject<TServiceModel>());  
               }  
             }  
             // server returned a single Entry JSON  
             else  
             {  
               modelList.Add(JToken.Parse(CustomizeEntryString(JToken.Parse(response.Data.ToString()).SelectToken(request.CollectionPath), request.ViewModelPath, request.ReplaceStrings)).ToObject<TServiceModel>());  
             }  
           }  
           catch (JsonSerializationException ex)  
           {  
             string msg = "Error in parsing REST json response.";  
             Logger.Error(string.Format("{0}\n{1}. Response: [{2}]", msg, ex.Message, response.Data.ToString()));  
             response.Status = Enums.ResponseStatus.Error;  
             response.Message = msg;  
           }  
         }  
       }  
       return new Response<List<TServiceModel>> { ServiceURI = response.ServiceURI, Data = modelList, Message = response.Message, Status = response.Status, MetaData = response.MetaData };  
     }  
     private static Response<object> GetRESTServiceResponse(string requestURI)  
     {  
       HttpWebRequest httpWebRequest = null;  
       ESBServiceResponseMetadata responseMetadata = new ESBServiceResponseMetadata();  
       HttpWebResponse httpWebResponse = null;  
       Stream responseStream = null;  
       StreamReader responseStreamReader = null;  
       StringBuilder httpActivityLog = new StringBuilder();  
       Response<object> response = new Response<object> { ServiceURI = requestURI, Data = string.Empty, Message = string.Empty };  
       bool tryAgain;  
       try  
       {  
         httpWebRequest = WebRequest.Create(requestURI) as HttpWebRequest;  
         Logger.Info(string.Format("HttpWebRequest object created for request [{0}].", requestURI));  
         LogHTTPHeaderData(httpWebRequest.Headers, httpActivityLog);  
         do  
         {  
           tryAgain = false;  
           try  
           {  
             httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;  
             LogHTTPHeaderData(httpWebResponse.Headers, httpActivityLog);  
             Logger.Info(httpActivityLog.ToString());  
             try  
             {  
               responseStream = httpWebResponse.GetResponseStream();  
               responseStreamReader = new StreamReader(responseStream);  
               response.Data = responseStreamReader.ReadToEnd();  
               JToken esbServiceToken = JToken.Parse(response.Data.ToString()).SelectToken("feed.ESBServiceResponseMetadata");  
               if (esbServiceToken != null)  
               {  
                 try  
                 {  
                   responseMetadata = esbServiceToken.ToObject<ESBServiceResponseMetadata>();  
                   response.MetaData = responseMetadata;  
                   if (responseMetadata.ResponseStatus == Enums.ESBResponseStatuses.TechnicalError)  
                   {  
                     if (responseMetadata.ResponseDescription.IndexOf("Failed to establish a backside connection") > -1)  
                     {  
                       tryAgain = true;  
                     }  
                   }  
                 }  
                 catch (JsonSerializationException ex)  
                 {  
                   string msg = "Error in parsing REST json response.";  
                   Logger.Error(string.Format("{0}\n{1}. Response: [{2}]", msg, ex.Message, response.Data.ToString()));  
                   response.Status = Enums.ResponseStatus.Error;  
                   response.Message = msg;  
                   Logger.Error(ex);  
                 }  
                 response.Status = responseMetadata.ResponseStatus == Enums.ESBResponseStatuses.Success ? Enums.ResponseStatus.Success : Enums.ResponseStatus.Error;  
                 //response.Message = Enums.GetEnumDescription(responseMetadata.ResponseDescription);  
                 response.Message = responseMetadata.ResponseDescription;  
               }  
               else  
               {  
                 response.Status = Enums.ResponseStatus.Error;  
                 response.Message = "Could not find path 'feed.ESBServiceResponseMetadata' in JSON string.";  
               }  
             }  
             catch (WebException ex)  
             {  
               if (ex.Message.ToLower().IndexOf("timed out") > -1)  
               {  
                 tryAgain = true;  
                 response.FailedAttempts++;  
                 Logger.Error(string.Format("Response timed out. Commencing retry number {0}.", response.FailedAttempts));  
                 Logger.Error(ex);  
               }  
             }  
           }  
           catch (WebException ex)  
           {  
             if (ex.Message.ToLower().IndexOf("timed out") > -1)  
             {  
               tryAgain = true;  
               response.FailedAttempts++;  
               Logger.Error(string.Format("Response timed out. Commencing retry number {0}.", response.FailedAttempts));  
               Logger.Error(ex);  
             }  
           }  
           catch (Exception ex)  
           {  
             Logger.Error(string.Format("Error in getting request's Response Stream object for [{0}].\n{1}\n{2}", requestURI, ex.Message, ex.InnerException != null ? ex.InnerException.Message : string.Empty));  
             Logger.Error(ex);  
             Logger.Info(httpActivityLog.ToString());  
             response.Status = Enums.ResponseStatus.Error;  
             response.Message = string.Format("Error in getting request's Response Stream object for [{0}].\n{1}\n{2}", requestURI, ex.Message, ex.InnerException != null ? ex.InnerException.Message : string.Empty);  
             response.Data = httpActivityLog.ToString();  
           }  
         }  
         while (tryAgain);  
       }  
       catch (Exception ex)  
       {  
         httpWebRequest.Abort();  
         Logger.Error(string.Format("Error in creating HttpWebRequest object for [{0}].", requestURI));  
         Logger.Error(ex);  
         Logger.Info(httpActivityLog.ToString());  
         response.Status = Enums.ResponseStatus.Error;  
         response.Message = string.Format("Error in creating HttpWebRequest object for [{0}].\n{1}", requestURI, ex.Message);  
         response.Data = httpActivityLog.ToString();  
       }  
       finally  
       {  
         if (httpWebResponse != null)  
           httpWebResponse.Close();  
         if (responseStream != null)  
           responseStream.Dispose();  
         if (responseStreamReader != null)  
           responseStreamReader.Dispose();  
       }  
       return response;  
     }  
     private static string CustomizeEntryString(JToken entry, string viewModelPath, List<string> replaceStrings)  
     {  
       string modelJsonStr = string.Empty;  
       modelJsonStr = entry.SelectToken(viewModelPath).ToString();  
       #region Remove unwanted characters  
       /*  
            * In case there are characters that need to be removed from the Json structure the service returns,  
            * so the names of the fields in the Json object match our model field names - and the cast/mapping works  
            */  
       foreach (string replaceStr in replaceStrings)  
       {  
         modelJsonStr = modelJsonStr.Replace(replaceStr, "");  
       }  
       #endregion Remove unwanted characters  
       return modelJsonStr;  
     }  
     private static void LogHTTPHeaderData(WebHeaderCollection webHeaderCollection, StringBuilder httpActivityLog)  
     {  
       for (int i = 0; i < webHeaderCollection.Count; ++i)  
         httpActivityLog.AppendLine(string.Format("\nHTTP header parameter:{0}, Value :{1}", webHeaderCollection.Keys[i], webHeaderCollection[i]));  
     }  
   }  
 }