Tricky C# Tasks : when do they start ?

For an integration test purpose, several tasks needed to start in parallel. The test failed because the tasks were started like this :

 class Program
    {
        static async Task Main(string[] args)
        {
            var functions = new Functions();
            var list = new List<Task>();
            list.Add(functions.DoSomething()); //task starting now !
            list.Add(functions.DoSomething()); //task starting now !
            list.Add(functions.DoSomething()); //task starting now !
            await Task.WhenAll(list);
        }
    }
    class Functions
    {
        public Task DoSomething()
        {
            Console.WriteLine($"{DateTime.Now} function executing : {nameof(DoSomething)}");
            Thread.Sleep(1000);
            return Task.FromResult(0);
        }
    }


Beware that the task starts as soon as it is returned to the caller, that is to say, as soon as you add it to the list, and not at the WhenAll call.
You don’t necessarily need to await to start the task. In this case, the tasks are all started as they are added to the list, even if no one is waiting for them yet. In other words, simply calling functions.DoSomething() does start the task.

If you wanna start them at the same time for real you may use:

  class Program
    {
        static void Main(string[] args)
        {
            var functions = new Functions();
            var lazyList = new List<Func<Task>>();
            lazyList.Add(() => functions.DoSomething());
            lazyList.Add(() => functions.DoSomething());
            lazyList.Add(() => functions.DoSomething());
            Parallel.Invoke(lazyList.Select(f => new Action(() => f())).ToArray());  // here they start
        }
    }
    class Functions
    {
        public Task DoSomething()
        {
            Console.WriteLine($"{DateTime.Now} function executing : {nameof(DoSomething)}");
            Thread.Sleep(1000);
            return Task.FromResult(0);
        }
    }

and if you still want to assert on your tasks after they all executed, you can use a real list of tasks this time to access them:

            var functions = new Functions();
            var lazyList = new List<Func<Task>>();
            lazyList.Add(() => functions.DoSomething());
            lazyList.Add(() => functions.DoSomething());
            lazyList.Add(() => functions.DoSomething());
            var list = new List();
            Parallel.Invoke(lazyList.Select(f => new Action(() => list.Add(f()))).ToArray());
            Console.WriteLine(list.First().Status);

And beware, tasks don’t necessarily mean multi threads 😉

Advertisements

ASP.NET Core : Overriding controller AuthorizeAttribute for just one action

In Asp.Net Core, creating custom authorize attributes is over. You’d better deal with policies now. If you want to override the controller AuthorizeAttribute for just one action with a stronger policy, nothing simpler :

[Authorize]
public class AuthorizedController : Controller
{
    [HttpGet("do-authorized-stuff/{id}")]
    public IActionResult Whatever(int id)
    { }

    [Authorize(Consts.AdminOnly)]
    [HttpGet("do-admin-stuff/{id}")]
    public IActionResult WhateverAdminStuff(int id)
    {
    }

}

But what if you want to override it with a weaker policy ? For example you want to override the admin policy just for one action which would be available to any authenticated user like so :

[Authorize(Consts.AdminOnly)]
public class AuthorizedController : Controller
{

    [Authorize] //admin policy is still applied :o(
    [HttpGet("do-authorized-stuff/{id}")]
    public IActionResult Whatever(int id)
    { }

    [HttpGet("do-admin-stuff/{id}")] //admin policy is applied
    public IActionResult WhateverAdminStuff(int id)
    { }

This won’t work as policies are accumulated, in other words it’s an AND not an OR.
Still, Asp.Net Core has a lot of possibilities and you could simply do in Startup.cs file :

services.AddAuthorization(options =>
{
    var simplyAuthenticated = new AuthorizationPolicy(new DenyAnonymousAuthorizationRequirementOverrideOthers().Yield(), new List<string>());
    options.AddPolicy(name: PolicyConsts.AuthenticatedOnlyOverridePolicyName, policy: simplyAuthenticated);
    options.AddPolicy(name: PolicyConsts.Admin, configurePolicy: policy => policy.RequireAssertion(e =>
    {
        if (e.Resource is AuthorizationFilterContext afc)
        {
            var noPolicy = afc.Filters.OfType<AuthorizeFilter>().Any(p => p.Policy.Requirements.Count == 1 && p.Policy.Requirements.Single() is DenyAnonymousAuthorizationRequirementOverrideOthers);
            if (noPolicy)
                return true;
        }
        return e.User.IsInRole(Consts.Admin);
    }));

}

and here’s the custom DenyAnonymousAuthorizationRequirementOverrideOthers :


namespace Whatever
{
    using Microsoft.AspNetCore.Authorization.Infrastructure;

    public class DenyAnonymousAuthorizationRequirementOverrideOthers : DenyAnonymousAuthorizationRequirement
    { }
}

 

and here’s the controller :


[Authorize(PolicyConsts.AdminOnly)]
public class AuthorizedController : Controller
{

    [Authorize(PolicyConsts.AuthenticatedOnlyOverride)]
    [HttpGet("do-authorized-stuff/{id}")]//admin policy is bypassed thanks to our startup.cs code
    public IActionResult Whatever(int id)
    { }

    [HttpGet("do-admin-stuff/{id}")] //admin policy is applied coming from controller
    public IActionResult WhateverAdminStuff(int id)
    { }
}

And you can add any custom policy you want and make it override others in the RequireAssertion Func of other policies.

Hope it helps, see ya !

TagHelpers : using the post method with html anchor tags

Want to make an anchor which would issue a post call to the server rather than a get ?

post anchor tag

instead of writing the ugly :

<form asp-controller="Account" asp-action="Logout" method="post" id="logout-form"></form>

 <a href="javascript:document.getElementById('logout-form').submit()"></a>

Why not use a tag helper to generate this behind the scenes and keep your html clean ? Here’s mine, I made it simple, your anchor must have an id, a href and a asp-post attribute to work with it.

    [HtmlTargetElement("a", Attributes = ForAttributeName + ",id,href")]
    public class PostAnchorTagHelper : TagHelper
    {
        private const string ForAttributeName = "asp-post";
        private IHtmlGenerator _generator;

        public PostAnchorTagHelper(IHtmlGenerator generator)
        {
            _generator = generator;
        }

        [HtmlAttributeName(ForAttributeName)]
        public string For { get; set; }

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

        public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var href = context.AllAttributes["href"].Value.ToString();
            var id = context.AllAttributes["id"].Value.ToString();
            output.Attributes.RemoveAll("href");
            var form = new TagBuilder("form");
            form.Attributes.Add("action", href);
            form.Attributes.Add("method", "post");
            form.Attributes.Add("asp-antiforgery", "true");
            var idForm = id + "-form";
            form.Attributes.Add("id", idForm);
            var antiforgeryTag = _generator.GenerateAntiforgery(ViewContext);
            if (antiforgeryTag != null)
            {
                form.InnerHtml.AppendHtml(antiforgeryTag);
            }
            output.Attributes.Add("href", $"javascript:document.getElementById('{idForm}').submit()");
            output.PostElement.AppendHtml(form);
            return base.ProcessAsync(context, output);
        }
    }

Now, just write :

<a id="logout" href="@Url.Action("Logout", "Account")" asp-post="true"></a>

And you’re good to go !

Localizing validation messages in asp.net core : fallback on shared resources if not found

Right now in asp.net core, when you want to localize your validation messages, you have to create a resource file with the exact same name as your viewmodel, as explained here : https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization#dataannotations-localization

What if you want to keep this smart mechanism but still get a fallback to a shared resource file ? For example, let’s say you want to have the message “This field is absolutely required please !” instead of the standard message, for all required fields in all your viewmodels. You obviously don’t want to specify this message in every localized resource file named like your viewmodel.

Here’s a basic solution. In the startup.cs file, add something like :

services.AddMvc().AddDataAnnotationsLocalization(o =>
o.DataAnnotationLocalizerProvider = (modelType, stringLocalizerFactory) =>
{
   var sl = stringLocalizerFactory.Create(modelType);
   var shared = stringLocalizerFactory.Create(typeof(SharedResources));
   var ezSl = new MyDataAnnotations.ResourceManagerStringLocalizer(sl, shared);
   return ezSl;
})

And this is how your class MyDataAnnotations.ResourceManagerStringLocalizer could look like :

Continue reading “Localizing validation messages in asp.net core : fallback on shared resources if not found”

Beware of IEnumerable with Entity Framework

Sometimes you want to build a generic data access layer out of your entity framework concrete context, and you want to use the precious generic method on the DbContext such as the generic Set(). If it can spare you some time… Beware than when you do :

         public IEnumerable<T> Get<T>() where T : class
        {
            return _context.Set<T>();
        }

When the function returns, your query is already materialized, the query to the database has been made ! Because the Get function’s return type, IEnumerable, implies an implicit cast to get it from a DbSet. EF is probably making a ToList() inside to transform to IEnumerable, and thus, making the roundtrip to database and materializing objects.

To keep a lazy set, it’s better to switch to  :

       public IQueryable<T> Get<T>() where T : class
        {
            return _context.Set<T>();
        }

 

Charger des scripts dans une vue partielle

En ASP.NET MVC,  il arrive que l’on veuille Ă©crire ou rĂ©fĂ©rencer des scripts depuis une vue partielle, afin de dĂ©lĂ©guer l’entiĂšre responsabilitĂ© de certains scripts et de leur fonctionnement Ă  ce bout de vue uniquement. Par exemple, une vue partielle destinĂ©e Ă  de la conversation instantanĂ©e, Ă  du « chat », prĂ©sentera tous les Ă©lĂ©ments UI nĂ©cessaires Ă  l’envoi et Ă  la rĂ©ception de messages instantanĂ©s si la personne est authentifiĂ©e et s’occupera Ă©galement de s’assurer que les bons scripts, tels que SignalR par exemple, ont Ă©tĂ© chargĂ©s et exĂ©cutĂ©s en vue du bon fonctionnement du chat.

Ces scripts nĂ©cessitent souvent que d’autres scripts aient Ă©tĂ© chargĂ©s au prĂ©alable, comme JQuery par exemple. Malheureusement, une vue partielle se situe souvent en milieu de document et les scripts de base, eux, souvent juste avant la balise de fermeture du body, afin de ne pas bloquer le rendu de la page et de conserver de bonnes performances. Sachant qu’ils se chargent et s’exĂ©cutent dans l’ordre de leur rĂ©fĂ©rencement, les scripts situĂ©s dans la vue partielle ne fonctionneront pas car les dĂ©pendances n’auront pas encore Ă©tĂ© chargĂ©es.

Ce problÚme se résout facilement dans une vue basique car il est possible de redéfinir une section en ASP.NET MVC : le développeur peut redéfinir la section « Scripts » qui se situerait aprÚs les scripts de base de fin de body. Mais dans une vue partielle, il est impossible de redéfinir une section. Ainsi, un script présent dans une vue partielle nécessitant JQuery risque de planter en disant que $ est « undefined ».

Continue reading “Charger des scripts dans une vue partielle”

Gestion des erreurs en ASP.NET MVC

Article publiĂ© dans le magazine Programmez ! – Avril 2014 – N° 173

Lors de la conception d’une application web, gĂ©rer le comportement du systĂšme en cas d’erreur est important et ne devrait pas ĂȘtre laissĂ© au hasard. D’une part, visualiser une page systĂšme peu accueillante, non stylisĂ©e, et loin des couleurs du site n’est pas toujours agrĂ©able pour l’utilisateur qui vient en plus de recevoir une erreur. D’autre part, si certains paramĂ©trages ne sont pas effectuĂ©s, l’utilisateur pourrait avoir accĂšs aux dĂ©tails techniques de l’exception. Un visiteur mal intentionnĂ© pourrait se servir de ces dĂ©tails pour accumuler des connaissances sur la structure technique du site et trouver plus facilement une faille de sĂ©curitĂ© Ă  exploiter. Ainsi, il appartient Ă  l’équipe de rĂ©alisation du site de dĂ©terminer en amont le niveau de dĂ©tails d’erreur Ă  dĂ©livrer Ă  l’utilisateur, les codes d’erreur HTTP Ă  renvoyer en fonction des situations et les pages d’erreur qui seront affichĂ©es. En somme, il s’agit de dĂ©cider d’un comportement applicatif en cas d’erreur qui soit uniformĂ©ment suivi au cours des dĂ©veloppements.

ASP.NET MVC offre de nombreuses solutions pour gĂ©rer une exception de maniĂšre centralisĂ©e et rediriger vers une ressource en particulier. GĂ©rer les erreurs peut donc se faire de plusieurs façons et peut s’adapter Ă  divers scĂ©narios. Ces diffĂ©rentes maniĂšres seront exposĂ©es au cours de cet article, du niveau le plus global Ă  celui le plus fin.

Continue reading “Gestion des erreurs en ASP.NET MVC”