Category: .NET Core

TinyTypes–adding equality operator–part 3

This is a series

  1. http://msprogrammer.serviciipeweb.ro/2018/03/12/tiny-types-in-cpart-1/
  2. http://msprogrammer.serviciipeweb.ro/2018/03/19/tiny-types-part-2adding-iequatable/
  3. http://msprogrammer.serviciipeweb.ro/2018/03/26/tinytypesadding-equality-operatorpart-3/

  4. http://msprogrammer.serviciipeweb.ro/2018/04/02/tiny-typesdocumentationpart-4/

Now we have come to the interesting part – the equality operator.

We have already operator equals, but not ==

 [TestMethod]
        public void TestSimpleIntOperatorEqual()
        {
            int nr = 7;
            TinyType<int> tt1 = nr;
            TinyType<int> tt2 = nr;

            Assert.AreEqual(tt1, tt2);
            Assert.IsFalse(tt1 == tt2);
        }
        [TestMethod]
        public void TestSimpleStringOperatorEqual()
        {
            string s = "http://msprogrammer.serviciipeweb.ro";
            TinyType<string> tt1 = s;
            TinyType<string> tt2 = s;

            Assert.AreEqual(tt1, tt2);
            Assert.IsFalse(tt1 == tt2);
        }

Because we primary want this TinyTypes for structs( int, double, bool, and so on) we can define a new class

For this we could implement operator  ==  by using the Equals operator ( because we KNOW that a struct cannot be null)

public class TinyTypeOf<T> : TinyType<T>
        where T:struct
    {
        public TinyTypeOf(T tDef) : base(tDef)
        {
        }

       
        public static bool operator ==(TinyTypeOf<T> lhs, TinyTypeOf<T> rhs)
        {
            if(lhs is null)
            {
                return rhs is null;
            }
            return lhs.t.Equals(rhs.t);
        }

        public static bool operator !=(TinyTypeOf<T> lhs, TinyTypeOf<T> rhs)
        {
            return !(lhs==rhs);
        }

CODE FOR NEW CLASS

Also, because the string is not a struct, but a class, we need for String also:

public class TinyTypeOfString : TinyType<string>

And the tests are

using Microsoft.VisualStudio.TestTools.UnitTesting;
using TinyTypesObjects;

namespace TinyTypesTest
{
    [TestClass]
    public class TestTinyTypeOperatorEqual
    {
        [TestMethod]
        public void TestSimpleIntOperatorEqual()
        {
            int nr = 7;
            TinyType<int> tt1 = nr;
            TinyType<int> tt2 = nr;

            Assert.AreEqual(tt1, tt2);
            Assert.IsFalse(tt1 == tt2);
        }
        [TestMethod]
        public void TestSimpleStringOperatorEqual()
        {
            string s = "http://msprogrammer.serviciipeweb.ro";
            TinyType<string> tt1 = s;
            TinyType<string> tt2 = s;

            Assert.AreEqual(tt1, tt2);
            Assert.IsFalse(tt1 == tt2);
        }
        [TestMethod]
        public void TestSimpleStringOperatorEqualWorks()
        {
            string s = "http://msprogrammer.serviciipeweb.ro";

            TinyTypeOfString tt1 = s;
            TinyTypeOfString tt2 = s;

            Assert.AreEqual(tt1, tt2);
            Assert.IsTrue(tt1 == tt2);
        }

        [TestMethod]
        public void TestSimpleIntOperatorEqualWorks()
        {
            int nr = 7;
            TinyTypeOf<int> tt1 = nr;
            TinyTypeOf<int> tt2 = nr;

            Assert.AreEqual(tt1, tt2);
            Assert.IsTrue(tt1 == tt2);
        }
        [TestMethod]
        public void TestSimpleIntNrOperatorEqualWorks()
        {
            int nr = 7;
            TinyType<int> tt1 = nr;
            
            Assert.AreEqual(tt1, nr);
            Assert.IsTrue(tt1 == nr);
        }
    }
}

Tiny Types part 2–adding IEquatable

This is a series

  1. http://msprogrammer.serviciipeweb.ro/2018/03/12/tiny-types-in-cpart-1/
  2. http://msprogrammer.serviciipeweb.ro/2018/03/19/tiny-types-part-2adding-iequatable/
  3. http://msprogrammer.serviciipeweb.ro/2018/03/26/tinytypesadding-equality-operatorpart-3/

  4. http://msprogrammer.serviciipeweb.ro/2018/04/02/tiny-typesdocumentationpart-4/

As always, the bigger problem is adding equality. The Tiny Type should be equal with the inner value – and with the other type with the same value. And, in C#, when you implement equality , there is a whole theory – see https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx .

So the code to define equality is 60 lines long just for defining equality for

public class TinyType<T>:IEquatable<T>, IEquatable<TinyType<T>>

   

But this is not all. This is the code for testing equality

using Microsoft.VisualStudio.TestTools.UnitTesting;
using TinyTypesObjects;

namespace TinyTypesTest
{
    [TestClass]
    public class TestTinyTypeEquals
    {
        [TestMethod]
        public void TestSimpleStringEquals()
        {
            #region arrange
            string s = "http://msprogrammer.serviciipeweb.ro";
            #endregion
            #region act
            TinyType<string> tt1 = s;
            TinyType<string> tt2 = s;
            #endregion
            #region assert
            Assert.IsTrue(tt1.Equals(tt2));
            Assert.AreEqual(tt1, tt2);
            Assert.AreEqual<string>(tt1, tt2);
            Assert.AreEqual<TinyType<string>>(tt1, tt2);
            Assert.AreEqual<string>(s, tt2);
            #endregion
        }
        [TestMethod]
        public void TestSimpleStringWithNull()
        {
            #region arrange
            string s = null;
            #endregion
            #region act
            TinyType<string> tt1 = s;
            TinyType<string> tt2 = null;
            #endregion
            #region assert
            Assert.IsFalse(tt1.Equals(tt2));
            Assert.AreNotEqual(tt1, tt2);
            Assert.AreEqual<string>(tt1, tt2);
            Assert.AreNotEqual<TinyType<string>>(tt1, tt2);
            Assert.AreEqual<string>(s, tt2);
            #endregion
        }
        [TestMethod]
        public void TestSimpleStringNull()
        {
            #region arrange
            string s = null;
            #endregion
            #region act
            TinyType<string> tt1 = s;
            TinyType<string> tt2 = s;
            #endregion
            #region assert
            Assert.IsTrue(tt1.Equals(tt2));
            Assert.AreEqual(tt1, tt2);
            Assert.AreEqual<string>(tt1, tt2);
            Assert.AreEqual<TinyType<string>>(tt1, tt2);
            Assert.AreEqual<string>(s, tt2);
            #endregion
        }
        [TestMethod]
        public void TestSimpleIntEquals()
        {
            #region arrange
            int s = 1;
            #endregion
            #region act
            TinyType<int> tt1 = s;
            TinyType<int> tt2 = s;
            #endregion
            #region assert
            Assert.IsTrue(tt1.Equals(tt2));
            Assert.AreEqual(tt1, tt2);
            Assert.AreEqual<int>(tt1, tt2);
            Assert.AreEqual<TinyType<int>>(tt1, tt2);
            Assert.AreEqual<int>(s, tt2);
            
            #endregion
        }

    }
}

Tiny types in C#–part 1

This is a series

  1. http://msprogrammer.serviciipeweb.ro/2018/03/12/tiny-types-in-cpart-1/
  2. http://msprogrammer.serviciipeweb.ro/2018/03/19/tiny-types-part-2adding-iequatable/
  3. http://msprogrammer.serviciipeweb.ro/2018/03/26/tinytypesadding-equality-operatorpart-3/

  4. http://msprogrammer.serviciipeweb.ro/2018/04/02/tiny-typesdocumentationpart-4/

I have read about tiny types in Javascript  – at https://darrenhobbs.com/2007/04/11/tiny-types/ and at https://janmolak.com/tiny-types-in-typescript-4680177f026e . It was an interesting idea – especially in this world of REST API .

I decided to make the same on C# – so here it is: https://github.com/ignatandrei/tinyTypes and at https://www.nuget.org/packages/TinyTypesObjects .

For the moment , the tests are minimal – just to get working :

using Microsoft.VisualStudio.TestTools.UnitTesting;
using TinyTypesObjects;

namespace TinyTypesTest
{
    [TestClass]
    public class TestTinyType
    {
        [TestMethod]
        public void TestConvert()
        {
            string s = "http://msprogrammer.serviciipeweb.ro";
            TinyType<string> tt = s;
            Assert.AreEqual(s, (string)tt);
        }
        [TestMethod]
        public void TestBehaviour()
        {
            #region arrange + act
            Author a1 = new Author("andrei", "ignat");
            Author a2 = new Author(
                new FirstName("andrei"),new LastName( "ignat"));

            Author a3 = new Author(firstName:"andrei",lastName: "ignat");
            #endregion
            #region assert
            Assert.AreEqual(a2.FullName(), a3.FullName());
            Assert.AreEqual(a2.FullName(), a3.FullName());
            #endregion

        }
    }
}

Next time , I will do IComparable / Iequatable /Equals and others

Country Tag Helper–part 5

Now the Country Tag Helper supports .NET Standard 2.0 . This means that support .NET Core 2.0 .

Also, I have made a NuGET package in order to install on every .NET Core 2.0 application with explanations: https://www.nuget.org/packages/CountryTagHelper 

The only trouble here was to add references and make documentation in VS2017 about package ( see https://docs.microsoft.com/en-us/nuget/guides/create-net-standard-packages-vs2017 )

As I said , the source is at GitHub https://github.com/ignatandrei/CountryTagHelper and demo at http://countrytaghelper.apphb.com/

What remains to be done:

localized
add select2 with image flags   

provider / dependency injection for FromIP
readme.md / wiki on github

Country Tag Helper–part 4

 

Now I want to see somewhere in order to prove it Ii s right. I choose https://appharbor.com/ because it is free – and can not beat free for free source.

I made github integration and modify the .csproj file accordingly to https://support.appharbor.com/discussions/problems/90387-is-net-core-supported-yet 

The result is at http://countrytaghelper.apphb.com/ 

Also , I have to modify the code source to get the ip of the user :

string GetUserIP(ViewContext vc)
        {
            StringValues headerFwd;
            if (ViewContext.HttpContext?.Request?.Headers?.TryGetValue("X-Forwarded-For", out headerFwd) ?? false)
            {
                string rawValues = headerFwd.ToString();
                if (!string.IsNullOrWhiteSpace(rawValues))
                {
                    return rawValues.Split(',')[0];
                }

            }
            return ViewContext.HttpContext?.Connection?.RemoteIpAddress?.ToString();

        }

where ViewContext is

 [ViewContext]
        public ViewContext ViewContext { get; set; }

Country Tag Helper – part 3

Adding empty item – easy peasy. Just append "<option selected style=’display: none’ value=”></option>";

 

if (ASPCountryEmpty)
                {
                    string empty = "<option selected style='display: none' value=''></option>";
                    output.Content.AppendHtml(empty);
                }

For getting the IP, I was trying to balance the 2 variants: downloading GeoIP database (https://dev.maxmind.com/geoip/geoip2/geolite2/) or calling http://freegeoip.net/ .

I do call http://freegeoip.net/ – and the next point is to use Dependency Injection … or a provider to do that.

For the moment, the code is:

 private async Task<string> GetCountryCodeFromIP(string ip)
        {
            //ip= "188.25.145.65";
            var url = "http://freegeoip.net/csv/"+ip;
            var request = WebRequest.Create(url);
            request.Method = "GET";
            using (var wr = await request.GetResponseAsync())
            {
                using (var receiveStream = wr.GetResponseStream())
                {
                    using (var reader = new StreamReader(receiveStream, Encoding.UTF8))
                    {
                        string content = reader.ReadToEnd();
                        return content.Split(',')[1];
                    }
                        
                }
                    
            }


        }

CountryTagHelper–part 2

Finally, I have arrived at functionality. I said that I want something like

<select asp-country="true" asp-country-selected="US" >
   
</select>

 

The functionality is very easy to be added:

The entire class is :

using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace CTHWeb.Controllers
{
    [HtmlTargetElement("select", Attributes = ASPCountryAttributeName)]
    public class CountryTagHelper:TagHelper
    {
        private const string ASPCountryAttributeName = "asp-country";
        private const string ASPCountrySelectedAttributeName = "asp-country-selected";

        [HtmlAttributeName(ASPCountryAttributeName)]
        public bool ASPCountry { get; set; }

        [HtmlAttributeName(ASPCountrySelectedAttributeName)]
        public string ASPCountrySelected{ get; set; }

        static string[] CountryISO;
        //static PropertyInfo[] properties;
        static CountryTagHelper()
        {
            var t =typeof( ResCTH);
            var properties= t.GetProperties(
                BindingFlags.Public |
                BindingFlags.Static |
                BindingFlags.GetProperty);
            CountryISO = properties
                .Where(it=>it.Name.Length==2)
                .Select(it => it.Name)
                .ToArray();
        }
        public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            if (ASPCountry)
            {
                bool existSelected = !string.IsNullOrWhiteSpace(ASPCountrySelected);
                string option = "<option value='{0}' {2}>{1}</option>";
                foreach (var item in CountryISO)
                {
                    string selected = "";
                    string localizedName = ResCTH.ResourceManager.GetString(item);
                    if (existSelected)
                    {
                        bool currentItem = string.Equals(item, ASPCountrySelected, StringComparison.CurrentCultureIgnoreCase);
                        currentItem = currentItem || string.Equals(localizedName, ASPCountrySelected, StringComparison.CurrentCultureIgnoreCase);
                        if (currentItem)
                            selected = "selected";
                    }
                    output.Content.AppendFormat(option, item, localizedName,selected);
                }
            }
            return base.ProcessAsync(context, output);
        }
    }
}

 

The important thing is that it works with other html attributes, like

<select asp-country="true" asp-country-selected="US" disabled=”disabled”>
   
</select>

 

What it remains to be done:

  1. from IP – recognize the country of the visitor from IP
  2. localized – display messages in other language
  3. first item empty – now it selects Andorra
  4. Add select2 with image flags
  5. NuGet package
  6. readme.md / wiki on github
  7. tests

Country tag helper–part 1

 

What I want to do is to create a Country Tag Helper for asp.net core.

Something like

<select asp-country=”true”

and then list all countries in this select. More, it should be localized ( Germany vs Allemagne). That means 1 or more resource files 

 

First I need initial data: The WorldBank API gives us the names http://api.worldbank.org/countries and I have made a project https://github.com/ignatandrei/WorldBankAPi that inspects that API.

To retrieve data in resource format , it is simple:

 var r = new CountriesRepository();
            var arr = r.GetCountries().Result.
                Select(it =>
                $"<data name = \"{it.iso2Code}\" xml:space=\"preserve\" >" +

                $"<value>{it.name}</value>" +
                "</data>").ToArray();
            var s = string.Join(Environment.NewLine, arr);
            File.WriteAllText(@"D:\c.txt", s);

 

Then we could copy paste into resources:

 <data name = "AW" xml:space="preserve" ><value>Aruba</value></data>
  <data name = "AF" xml:space="preserve" ><value>Afghanistan</value></data>
  <data name = "AO" xml:space="preserve" ><value>Angola</value></data>
 ...

 

Now I want to make a list of those names – so I need this code to translate between C# generated code from resource

public static string AD {
            get {
                return ResourceManager.GetString("AD", resourceCulture);
            }
        }
public static string AE {
            get {
                return ResourceManager.GetString("AE", resourceCulture);
            }
        }
...

 

and a list of names to be put into select.

Reflection to the rescue:

  var t =typeof( ResCTH);
            properties= t.GetProperties(
                BindingFlags.Public |
                BindingFlags.Static |
                BindingFlags.GetProperty);
            CountryISO = properties
                .Where(it=>it.Name.Length==2)
                .Select(it => it.Name)
                .ToArray();

 

The code is on github https://github.com/ignatandrei/CountryTagHelper and I will improve next time.

Making any call to a function of an object thread safe

 

 

I was wondering how to modify old code to support threads /task .

So I was making a small project about making any function of an object thread safe.

NuGet Package at : https://www.nuget.org/packages/ThreadSafeObject/

The solution contain tests and 2 console project for .NET Core and .NET Framework 4.5.1

The usage is pretty easy

Let’s say you have this

Calculation c = new Calculation();
c.Add();

And you want

c.Add

to be thread safe

In the Package Manager Console write:

Install-Package ThreadSafeObject

Then modify your code to:

Calculation c = new Calculation();
dynamic ts = new ThreadSafe(c);
ts.Add();

It is a toy project- however, it has tests to prove it is correct.

You can download the code source from https://github.com/ignatandrei/ThreadSafeObject/

InternalsVisibleTo usage

I do not know if you know  InternalsVisibleToAttribute

https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396  .

I have used on a project https://github.com/ignatandrei/WorldBankAPi , that deals with World Bank API to get information

Let’s take an example: Countries: http://api.worldbank.org/countries?format=json

I have had a class CountriesRepository, that deals with transforming countries Json from WorldBank to Country classes. How can I test it WITHOUT relying on reading data from the internet ?

Simple:

1. Make an interface to grab JSON , IJSonData

    public interface IJsonData
    {
        Task<string> JsonData(int page = 1);
    }

2. Make a class JSONCountry that reads JSON from HTTP and implements IJSonData

3.  Make the CountryRepository class with 2 constructors  – one with IJSonData and the default calling JSONCountry

[assembly: InternalsVisibleTo("WorldBank.Test")]
namespace WorldBank.Repository
{
    public class CountriesRepository
    {
        private IJsonData data;
        public CountriesRepository():this(new JsonCountries())
        {

        }
//this is internal - could not see by outside projects with exception of WorldBank.Test
//see InternalsVisibleTo above
        internal CountriesRepository(IJsonData data)
        {
            this.data = data;
        }
    }
}

Optional: when testing manually, save the json into same files on the hard disk – you will need for point 5

4. The code that will use CountryRepository in official way will use the default – will not see the one with IJSonData

var c = new CountriesRepository();
var data = c.GetCountries().Result;

5. The testing code will define another class JSONFromHard that implements IJSonData and reads data from Hard

class JsonFromHard : IJsonData
{
//code to read files from disk 
// you have saved those when testing manually 
//see point 2 the optional part 
}

6. Because I have defined InternalsVisibleToAttribute to the WorldBank.Test, the Test dll can see the constructor with IJSonData  and then it class with JSONFromHard   – and I have no dependecy on the Http

 

public void GetAndInterpretData()
        {
            //uses [assembly: InternalsVisibleTo("WorldBank.Test")]
            var c = new CountriesRepository(new JsonFromHard("Countries"));
            var data = c.GetCountries().Result;
            Assert.Equal(218, data.Length);
        }

You can download the project from https://github.com/ignatandrei/WorldBankAPi  to see working.