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.

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.