Cookies op Tweakers

Tweakers maakt gebruik van cookies, onder andere om de website te analyseren, het gebruiksgemak te vergroten en advertenties te tonen. Door gebruik te maken van deze website, of door op 'Ga verder' te klikken, geef je toestemming voor het gebruik van cookies. Wil je meer informatie over cookies en hoe ze worden gebruikt, bekijk dan ons cookiebeleid.

Meer informatie
sluiten

Onderzoek Tweakers

Om beter te begrijpen wat jij waardevol vindt aan Tweakers en andere online content of features, voeren we een onderzoek uit. Wil jij ons helpen door de bijbehorende vragenlijst in te vullen? Het invullen neemt ongeveer 15 minuten in beslag en onder alle deelnemers verloten we drie Tweakers-goodiebags.

Naar het onderzoek

Toon posts:

.Net Core 3 model validation voor type int

Pagina: 1
Acties:

Vraag


  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
Hi allemaal,

Ik ben bezig met een project in .Net core 3, echter ben ik al voor weken aan het stoeien met de model validation. Ik heb een formulier waarbij gebruikers een product kunnen bestellen.

Daarbij heb ik ook een type 'int' waar de gebruiker de aantal kan opgeven hoeveel hij er wilt bestellen.
Nu valt het mij op .net core netjes een foutmelding teruggeeft als er een verkeerde type wordt opgegeven, bijvoorbeeld een 'string'
The value 'aa' is not valid for Aantal.
Uiteraard is dat wel een gewenst resultaat, maar ik vind de melding niet zo netjes. Is het mogelijk om een foute type in een model op te vangen en waar een nette melding van te maken zoals 'Aantal moet een getal zijn'

Of is het mogelijk / makkelijker om de tekst te overschrijven?

Verder heb ik geprobeerd om een custom 'ValidationAttribute' te schrijven, met de hoop dat hij eerst de validaties zou controleren voordat hij voor datatype zou kijken. Helaas heeft hij daar geen gehoor op, een Regex als ValidationAttribute heeft ook niet tot een succes kunnen resulteren.


C#:
1
2
3
 [Display(Name = "Aantal")]
        [Range(1, 2)]
        public int Quantity { get; set; }



Hebben jullie een idee / tips hoe ik dit op een nette manier zou kunnen oplossen?

[Voor 4% gewijzigd door kevinkrs op 05-01-2020 11:15]

Alle reacties


Acties:
  • +2Henk 'm!

  • Styxxy
  • Registratie: augustus 2009
  • Laatst online: 11:48
Probeer eens het volgende: MSDN: Customization And Localization Of ASP.NET Core MVC Default Validation E...
Is wel voor een oude netcore editie, maar mogelijks nog steeds geldig.

In kort, voeg volgende code toe aan startup:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);
 
    services.AddMvc(o =>
    {
        o.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
            value =>
            SiteResources.CustomImplicitRequired;
        o.ModelBindingMessageProvider.AttemptedValueIsInvalidAccessor =
            (value, fieldName) => 
            string.Format(SiteResources.CustomInvalidValue, value);
        o.ModelBindingMessageProvider.ValueMustBeANumberAccessor =
            fieldName => 
                string.Format(SiteResources.CustomNumeric, fieldName);
    });
}


En resource file:

https://msdnshared.blob.core.windows.net/media/2017/05/pic_3.png

  • Ben(V)
  • Registratie: december 2013
  • Laatst online: 14:46
De beste oplossing voor dit soort problemen is de validatie zelf te doen.
Dus gewoon een string invoerveld nemen en controleren of de input numeriek is en zelf een melding geven als dit niet het geval is.

Validatie van een gebruikers interface moet je altijd zelf in de hand houden.

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
@Styxxy Ik ben bezig met de implementatie, tot nu toe nog niet succesvol, moet trouwens wel toegeven dat lokalisering in .net core enorm 'messy' aanvoelt tegenover andere frameworks.

@Ben(V) Oef, ik laat de validatie het liefst altijd door de Modelstate.Isvalid valideren, puur omdat het simpel en schoon is. Stel je hebt een model met 4 keer een int krijg je 4 keer een check in je controller. Ik hoopte eigenlijk dat .net core iets had waarmee je een Error parameter mee kon sturen. Bijvoorbeeld:


C#:
1
2
[Datatype(ErrorMessage = "Moet een getal zijn")]
public int Quantity { get; set; }



Nu heb ik behoorlijk wat onderzoek gedaan, maar dat lijkt helaas niet te kunnen, enige optie dat overblijft is dus de teksten vertalen.

  • Ben(V)
  • Registratie: december 2013
  • Laatst online: 14:46
Je moet het natuurlijk zelf weten, maar normaal gesproken houd je validaties in eigen hand.

Heel erg vaak kom je namelijk na de eerste gebruikers testen erachter dat er allerlei vreemde invoer krijgt die je programma nooit verwacht.
Als je dan achteraf weer valdaties moet toevoegen komt dat de kwaliteit meestal niet ten goede.

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


  • R4gnax
  • Registratie: maart 2009
  • Laatst online: 30-03 19:52
Ben(V) schreef op maandag 6 januari 2020 @ 20:08:
Je moet het natuurlijk zelf weten, maar normaal gesproken houd je validaties in eigen hand.
Niet in ASP.NET (Core) MVC dus.

Daar heb je een collectie standaard validators, alsmede een base class (en een interface mocht je nog meer kaal willen gaan) om ook zelf validators te kunnen schrijven. Deze zijn bedoeld als constraint validators.

Heb je complexe domein-logica te valideren, dan dien je dat te doen door bijv. op je model de IValidatableObject interface te implementeren.

Pas als laatste komt het implementeren van losstaande imperatieve validatie-logica in bijv. de controller method.


Geen van allen speelt hier alleen, want de melding die kevinkrs hier krijgt vindt de oorsprong in een niet compatibel type en wordt direct door de default model binder van ASP.NET Core gegenereerd. Sommige zaken zijn nou eenmaal niet op een System.Int32 te projecteren en dan genereert de model binder zelf, intern, een model validatie fout.

De juiste manier om die meldingen aan te passen is al gegeven: dat doe je door de Mvc service juist in te stellen in je Startup.

  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
@R4gnax Bedankt voor je uitleg, betreft mvc en model-binding neem je mij de woorden uit de mond.

Tevens wil ik jullie bedanken, ik ben een heel eind in de juiste richting geholpen. De model errors kunnen / zijn nu vertaald, wat twee vliegen in 1 klap slaat.

Het is dan ook niet meer nodig om bij required bij elk veld een aparte ErrorMessage op te geven :)

https://www.imgdumper.nl/uploads9/5e13946053c3a/5e1394604da2c-Schermafbeelding_2020-01-06_om_21.10.49.png

https://www.imgdumper.nl/uploads9/5e1395a555c84/5e1395a54d5fc-Schermafbeelding_2020-01-06_om_21.16.29.png

Tevens ben ik er nog niet, ik vind dit nog erg rommelig ogen, is er een manier om dit netjes in een resource file te krijgen of is dit 'the way to go'?

  • Haan
  • Registratie: februari 2004
  • Laatst online: 30-03 13:49

Haan

dotnetter

Nee dat is inderdaad niet the way to go, zoek maar naar 'middleware' 😉

Kater? Eerst water, de rest komt later
Last.fm profiel


  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
Haan schreef op maandag 6 januari 2020 @ 21:24:
Nee dat is inderdaad niet the way to go, zoek maar naar 'middleware' 😉
https://www.imgdumper.nl/uploads9/5e1397c239786/5e1397c234205-Schermafbeelding_2020-01-06_om_21.25.20.png

Toevallig recentelijk gebruikt :p Ik zal even op zoek gaan naar betere oplossingen. Tips zijn altijd welkom trouwens!

  • R4gnax
  • Registratie: maart 2009
  • Laatst online: 30-03 19:52
Haan schreef op maandag 6 januari 2020 @ 21:24:
Nee dat is inderdaad niet the way to go, zoek maar naar 'middleware' 😉
Middleware is hier niet wat je wilt. Wat je wilt is een custom PostConfigure van de MvcOptions instance, die de IStringLocalizerFactory als dependency neemt, en vervolgens zo afdwingen dat de localisatie correct opgezet is.

Voorbeeldje:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class ModelBinderLocalizationExtensions 
{
  public static IServiceCollection AddModelBinderLocalization(
    this IServiceCollection services,
    Type resourceSource
  )
  {
    services.PostConfigure<MvcOptions, IStringLocalizerFactory>((options, factory) => {
      string Localize(string name, params object[] args) =>
        factory.Create(resourceSource).GetString(name, args);

      var provider = options.ModelBindingMessageProvider;

      provider.SetMissingKeyOrValueAccessor(name => Localize(name));
      provider.SetValueIsInvalidAccessor(name => Localize(name));
        
      // etc.
    });

    return services;
  }
}



Zoiets zou, als ik me goed herinner, de truc moeten doen.

[EDIT]
Met een local function kun je e.e.a. nog wat verder DRY maken.

[Voor 23% gewijzigd door R4gnax op 07-01-2020 00:55]


  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
R4gnax schreef op maandag 6 januari 2020 @ 22:52:
[...]


Middleware is hier niet wat je wilt. Wat je wilt is een custom PostConfigure van de MvcOptions instance, die de IStringLocalizerFactory als dependency neemt, en vervolgens zo afdwingen dat de localisatie correct opgezet is.

Voorbeeldje:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class ModelBinderLocalizationExtensions 
{
  public static IServiceCollection AddModelBinderLocalization(
    this IServiceCollection services,
    Type resourceSource
  )
  {
    services.PostConfigure<MvcOptions, IStringLocalizerFactory>((options, factory) => {
      string Localize(string name, params object[] args) =>
        factory.Create(resourceSource).GetString(name, args);

      var provider = options.ModelBindingMessageProvider;

      provider.SetMissingKeyOrValueAccessor(name => Localize(name));
      provider.SetValueIsInvalidAccessor(name => Localize(name));
        
      // etc.
    });

    return services;
  }
}



Zoiets zou, als ik me goed herinner, de truc moeten doen.

[EDIT]
Met een local function kun je e.e.a. nog wat verder DRY maken.
Goedemorgen,

Ik heb je oplossing geprobeerd, wat er trouwens zeer goed uit ziet. echter, kan het kloppen dat je IStringLocalizerFactory niet als 2e parameter kan meegeven?

Als ik de 2e parameter weghaal en hem injecteer door services.BuildServiceProvider().GetService<IStringLocalizerFactory>(); compileert de code wel.

Wat ik nu heb:


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    public static class ModelBinderLocalizationExtensions 
    {
        public static IServiceCollection AddModelBinderLocalization(
            this IServiceCollection services,
            Type resourceSource
        )
        {
            var factory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
                
            services.PostConfigure<MvcOptions>((options) => {
               
                options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) =>
                { 
                    var localizer = factory.Create(resourceSource); 
                    return localizer["ValueIsInvalid", x, y]; 
                });

            });

            return services;
        }
    }



De code compileert nu goed, alleen krijg ik nu 'ValueIsInvalid' als modelstate error terug, wat mij doet vermoeden dat hij het bestand of key niet kan vinden.

  • Haan
  • Registratie: februari 2004
  • Laatst online: 30-03 13:49

Haan

dotnetter

R4gnax schreef op maandag 6 januari 2020 @ 22:52:
[...]

Middleware is hier niet wat je wilt. Wat je wilt is een custom PostConfigure van de MvcOptions instance, die de IStringLocalizerFactory als dependency neemt, en vervolgens zo afdwingen dat de localisatie correct opgezet is.

Voorbeeldje:
<snip>
Zoiets zou, als ik me goed herinner, de truc moeten doen.
Ja sorry zoiets als jouw voorbeeld bedoelde ik ook, maar dat is dus geen middleware |:(

Kater? Eerst water, de rest komt later
Last.fm profiel


  • gorgi_19
  • Registratie: mei 2002
  • Nu online

gorgi_19

Kruimeltjes zijn weer op :9

R4gnax schreef op maandag 6 januari 2020 @ 20:18:
[...]


Niet in ASP.NET (Core) MVC dus.

Daar heb je een collectie standaard validators, alsmede een base class (en een interface mocht je nog meer kaal willen gaan) om ook zelf validators te kunnen schrijven. Deze zijn bedoeld als constraint validators.

Heb je complexe domein-logica te valideren, dan dien je dat te doen door bijv. op je model de IValidatableObject interface te implementeren.
Als je meer in detail wilt valideren, kan je ook iets als Fluentvalidation gebruiken. Je splitst hiermee bovendien je model en validatie van elkaar.

Digitaal onderwijsmateriaal, leermateriaal voor hbo


  • R4gnax
  • Registratie: maart 2009
  • Laatst online: 30-03 19:52
kevinkrs schreef op dinsdag 7 januari 2020 @ 09:34:
Ik heb je oplossing geprobeerd, wat er trouwens zeer goed uit ziet. echter, kan het kloppen dat je IStringLocalizerFactory niet als 2e parameter kan meegeven?
Mijn fout. Vergeten dat de PostConfigure syntax met dependencies enkel werkt via de uitgebreide OptionsBuilder API. Probeer het eens zo:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static class ModelBinderLocalizationExtensions 
{
  public static IServiceCollection AddModelBinderLocalization(
    this IServiceCollection services,
    Type resourceSource
  ) => services
    .AddOptions<MvcOptions>
    .PostConfigure<IStringLocalizerFactory>((options, factory) => {
      string Localize(string name, params object[] args) =>
        factory.Create(resourceSource).GetString(name, args);

      var provider = options.ModelBindingMessageProvider;

      provider.SetMissingKeyOrValueAccessor(name => Localize(name));
      provider.SetValueIsInvalidAccessor(name => Localize(name));
        
      // etc.
    })
    .Services;
}
kevinkrs schreef op dinsdag 7 januari 2020 @ 09:34:

Als ik de 2e parameter weghaal en hem injecteer door services.BuildServiceProvider().GetService<IStringLocalizerFactory>(); compileert de code wel.
Pas op! Microsoft zelf raadt het ten zeerste af om deze method aan te roepen in enige logica die binnen ConfigureServices draait.Op die manier BuildServiceProvider() gebruiken creeërt namelijk een service provider in een niet gefinaliseerde staat. Je hebt geen enkele garantie dat zaken goed resolven uit die provider.
kevinkrs schreef op dinsdag 7 januari 2020 @ 09:34:

De code compileert nu goed, alleen krijg ik nu 'ValueIsInvalid' als modelstate error terug, wat mij doet vermoeden dat hij het bestand of key niet kan vinden.
Klopt, je hebt de localization instellingen zelf wss. nog niet goed ingeregeld.

  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
R4gnax schreef op dinsdag 7 januari 2020 @ 19:48:
[...]


Mijn fout. Vergeten dat de PostConfigure syntax met dependencies enkel werkt via de uitgebreide OptionsBuilder API. Probeer het eens zo:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static class ModelBinderLocalizationExtensions 
{
  public static IServiceCollection AddModelBinderLocalization(
    this IServiceCollection services,
    Type resourceSource
  ) => services
    .AddOptions<MvcOptions>
    .PostConfigure<IStringLocalizerFactory>((options, factory) => {
      string Localize(string name, params object[] args) =>
        factory.Create(resourceSource).GetString(name, args);

      var provider = options.ModelBindingMessageProvider;

      provider.SetMissingKeyOrValueAccessor(name => Localize(name));
      provider.SetValueIsInvalidAccessor(name => Localize(name));
        
      // etc.
    })
    .Services;
}



[...]


Pas op! Microsoft zelf raadt het ten zeerste af om deze method aan te roepen in enige logica die binnen ConfigureServices draait.Op die manier BuildServiceProvider() gebruiken creeërt namelijk een service provider in een niet gefinaliseerde staat. Je hebt geen enkele garantie dat zaken goed resolven uit die provider.


[...]


Klopt, je hebt de localization instellingen zelf wss. nog niet goed ingeregeld.
Is eigenlijk alles dat in Startup nog uitgevoerd nog niet gefinaliseerde staat? Aangezien wellicht nog niet alle services resolved zijn.

Ik heb trouwens je code geprobeerd, en een klein beetje aangepast :p

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public static class ModelBinderLocalizationExtensions 
    {
        public static IServiceCollection AddModelBinderLocalization(this IServiceCollection services, Type resourceSource) => services
            .AddOptions<MvcOptions>()
            .PostConfigure<IStringLocalizerFactory>((options, factory) => {
                string Localize(string name, params object[] args) =>
                    factory.Create(resourceSource).GetString(name, args);

                var provider = options.ModelBindingMessageProvider;

                // provider.SetMissingKeyOrValueAccessor(name => Localize(name));
                provider.SetAttemptedValueIsInvalidAccessor((name, y) => Localize("SetAttemptedValueIsInvalidAccessor"));
            })
            .Services;
    }


Even het volgende veranderd:

C#:
1
provider.SetAttemptedValueIsInvalidAccessor((name, y) => Localize("SetAttemptedValueIsInvalidAccessor"));


Ik heb de parameter name veranderd naar een "SetAttemptedValueIsInvalidAccessor", anders zoekt hij letterlijk naar de name van het veld van de Model.

Hij compileert, dat is een goed teken, de resources lijken nog steeds niet te werken... Ik wil er wel bij zeggen dat ik Rider gebruik (MacOS), kwam er vandaag ook achter dat ik handmatig de 'resource.Designer.cs' vanuit de IDE moest genereren.

Ik heb twee bestanden:

- Shared.resx
- Shared.en.resx

https://www.imgdumper.nl/uploads9/5e14f5941492a/5e14f5940e33f-Schermafbeelding_2020-01-07_om_22.18.05.png

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>

<root>
    <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
        <xsd:element name="root" msdata:IsDataSet="true">
            <data name="SetAttemptedValueIsInvalidAccessor">
                <value>Waarde is niet geldig.</value>
            </data>
        </xsd:element>
    </xsd:schema>
    <resheader name="resmimetype">
        <value>text/microsoft-resx</value>
    </resheader>
    <resheader name="version">
        <value>1.3</value>
    </resheader>
    <resheader name="reader">
        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
    </resheader>
    <resheader name="writer">
        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
    </resheader>
</root>


De designer van Shared is trouwens netjes gevuld en bevat onder andere:

C#:
1
2
3
4
5
        internal static string SetAttemptedValueIsInvalidAccessor {
            get {
                return ResourceManager.GetString("SetAttemptedValueIsInvalidAccessor", resourceCulture);
            }
        }


De designer van Shared.en blijft leeg, wat opzicht logisch is anders zou je twee referenties naar dezelfde class hebben?


Wat ik ook doe, hij blijft zeggen 'Resources not found' Enige idee/ suggestie hoe dit komt, ben ik wat vergeten? Zie ik iets over het hoofd?

  • RobIII
  • Registratie: december 2001
  • Laatst online: 15:50

RobIII

Admin Devschuur®

^ Romeinse 3 ja!

kevinkrs schreef op dinsdag 7 januari 2020 @ 22:16:
Wat ik ook doe, hij blijft zeggen 'Resources not found' Enige idee/ suggestie hoe dit komt, ben ik wat vergeten? Zie ik iets over het hoofd?
Zet je de resources ook op "Compile" (of "Embedded Resource" o.i.d.) in je project? (Even uit m'n hoofd...)

[Voor 4% gewijzigd door RobIII op 07-01-2020 22:48]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Roses are red Violets are blue, Unexpected ‘{‘ on line 32.

Over mij


  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
RobIII schreef op dinsdag 7 januari 2020 @ 22:47:
[...]

Zet je de resources ook op "Compile" (of "Embedded Resource" o.i.d.) in je project? (Even uit m'n hoofd...)
Hij staat op embeded resource, compile geeft fouten op elke line.

Edit: Ik zie de foutmelding trouwens in de debugger als ik de GetAllStrings() methode gebruik.

[Voor 14% gewijzigd door kevinkrs op 07-01-2020 23:05]


  • R4gnax
  • Registratie: maart 2009
  • Laatst online: 30-03 19:52
kevinkrs schreef op dinsdag 7 januari 2020 @ 22:16:
[...]

Ik heb de parameter name veranderd naar een "SetAttemptedValueIsInvalidAccessor", anders zoekt hij letterlijk naar de name van het veld van de Model.
Yup. Duidelijk geval van een caffeïne-gebrek aan mijn kant. Sorry. :$
Is eigenlijk alles dat in Startup nog uitgevoerd nog niet gefinaliseerde staat? Aangezien wellicht nog niet alle services resolved zijn.
Ja. Dat is inderdaad waarvan je uit moet gaan.

  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
R4gnax schreef op woensdag 8 januari 2020 @ 20:43:
[...]


Yup. Duidelijk geval van een caffeïne-gebrek aan mijn kant. Sorry. :$


[...]


Ja. Dat is inderdaad waarvan je uit moet gaan.
Zit nog steeds met de issue waarbij ik altijd de key terug krijg vanuit de resource file ipv een Value.

Ik heb nog even verder gezocht...

https://github.com/dotnet/sdk/issues/4033

https://github.com/dotnet/aspnetcore/issues/18026

Zou dit een bug kunnen zijn?

  • R4gnax
  • Registratie: maart 2009
  • Laatst online: 30-03 19:52
Kan ik zo niet direct zeggen.
Ik weet wel dat een collega van mij eerder ook hoopjes problemen heeft gehad om in .Net Core standaard resx-sourced resource strings werkend te krijgen. Ik kan hem op z'n vroegst komende maandag pas weer te pakken krijgen, maar toch: als je wilt zal ik eens uitvragen hoe het hem uiteindelijk gelukt was.

  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
R4gnax schreef op donderdag 9 januari 2020 @ 20:14:
[...]


Kan ik zo niet direct zeggen.
Ik weet wel dat een collega van mij eerder ook hoopjes problemen heeft gehad om in .Net Core standaard resx-sourced resource strings werkend te krijgen. Ik kan hem op z'n vroegst komende maandag pas weer te pakken krijgen, maar toch: als je wilt zal ik eens uitvragen hoe het hem uiteindelijk gelukt was.
Als je het wilt navragen zal ik je heel dankbaar zijn, ik loop hierop nog steeds vast en ik heb inmiddels alles al geprobeerd wat je maar kunt bedenken...

Ik kan wel tot maandag wachten :)

  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
R4gnax schreef op donderdag 9 januari 2020 @ 20:14:
[...]


Kan ik zo niet direct zeggen.
Ik weet wel dat een collega van mij eerder ook hoopjes problemen heeft gehad om in .Net Core standaard resx-sourced resource strings werkend te krijgen. Ik kan hem op z'n vroegst komende maandag pas weer te pakken krijgen, maar toch: als je wilt zal ik eens uitvragen hoe het hem uiteindelijk gelukt was.
Ik denk dat ik erachter ben waarom het niet werkt.

code:
1
 app.UseRequestLocalization(app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value);


moet geset zijn om te betalen welke resource bestand hij moet gebruiken. Ik vermoed alleen dat hij het vertalen van de models eerder doet dan het setten van de culture en taal, waardoor hij niet de vertalingen kan vinden....

  • kevinkrs
  • Registratie: juni 2010
  • Laatst online: 14:50
Goedemorgen,

met bloed zweet en tranen en een bijna uit het raam vliegende MacBook Pro heb ik het werkend.

Even een korte uitleg wat ik gedaan heb.

In Startup moet het volgende toegevoegd worden:

C#:
1
services.AddLocalization(options => { options.ResourcesPath = "Resources"; });

Dit is belangrijk, hij lijkt de path niet te kunnen vinden zonder deze in startup de definiëren.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
services.Configure<RequestLocalizationOptions>(options =>
            {
                var supportedCultures = new[]
                {
                    new CultureInfo("en"),
                    new CultureInfo("nl")
                };

                options.DefaultRequestCulture = new RequestCulture(culture: "en", uiCulture: "en");
                options.SupportedUICultures = supportedCultures;
                options.SupportedCultures = supportedCultures;
            });

Je moet een lijst van cultures meegeven, als je browser-taal niet in de lijst staat kiest hij voor de default Engelse vertalingen.

C#:
1
services.AddControllersWithViews()

Uiteraard is deze methode nu weer netjes leeg vergeleken met de code van de vorige posts :p

C#:
1
services.AddModelBinderLocalization(typeof(SharedResources));

Uiteraard de Extensions class als service registreren.

C#:
1
app.UseRequestLocalization(app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value);


Dit is belangrijk anders wordt je culture niet geset en weet c# niet welk taalbestand hij moet selecteren.
Tevens is het belangrijk om te weten dat je taalbestanden niet meer generiek mogen zijn. Het MOET myresource.nl.resx en myresource.en.resx zijn.

Vervolgens heb ik de ModelBinderLocalizationExtensions class aangepast:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 public static class ModelBinderLocalizationExtensions 
    {
        public static IServiceCollection AddModelBinderLocalization(this IServiceCollection services, Type resourceSource) => services
            .AddOptions<MvcOptions>()
            .PostConfigure<IStringLocalizerFactory>((options, factory) => {
                var assemblyName = new AssemblyName(resourceSource.GetTypeInfo().Assembly.FullName);
                var localize = factory.Create("SharedResources", assemblyName.Name);

                string Localize(string name, params object[] args) =>
                    localize.GetString(name, args);

                var provider = options.ModelBindingMessageProvider;

                provider.SetValueIsInvalidAccessor(
                    (value) => Localize("SetAttemptedValueIsInvalidAccessor", value));

                provider.SetAttemptedValueIsInvalidAccessor(
                    (name, value) => Localize("SetAttemptedValueIsInvalidAccessor", name, value));

                provider.SetValueMustBeANumberAccessor(
                    (name) => Localize("SetAttemptedValueIsInvalidAccessor", name));

                provider.SetMissingBindRequiredValueAccessor(
                    (name) => Localize("SetAttemptedValueIsInvalidAccessor", name));

                provider.SetAttemptedValueIsInvalidAccessor(
                    (name, value) => Localize("SetAttemptedValueIsInvalidAccessor", name, value));

                provider.SetMissingKeyOrValueAccessor(
                    () => Localize("SetAttemptedValueIsInvalidAccessor"));

                provider.SetUnknownValueIsInvalidAccessor(
                    (name) => Localize("SetAttemptedValueIsInvalidAccessor", name));

                provider.SetValueMustNotBeNullAccessor(
                    (name) => Localize("SetAttemptedValueIsInvalidAccessor", name));

                provider.SetMissingRequestBodyRequiredValueAccessor(
                    () => Localize(""));
                
                provider.SetNonPropertyAttemptedValueIsInvalidAccessor(
                    (name) => Localize("", name));
                
                provider.SetNonPropertyAttemptedValueIsInvalidAccessor(
                    (name) => Localize("", name));

                provider.SetNonPropertyValueMustBeANumberAccessor(
                    () => Localize(""));


                // provider.SetAttemptedValueIsInvalidAccessor((name, value) => Localize("SetAttemptedValueIsInvalidAccessor", name, value));
            })
            .Services;

Ik moet hiervan de strings nog even veranderen, maar ik was nog bezig met trial & error :p

In de class heb ik het volgende veranderd:
C#:
1
2
 var assemblyName = new AssemblyName(resourceSource.GetTypeInfo().Assembly.FullName);
                var localize = factory.Create("SharedResources", assemblyName.Name);


Je moet de assembly path opvragen en een fasename opgeven, anders kan hij je resource bestand niet vinden.

Vervolgens in de resource bestanden:

SharedResources.en.resx
code:
1
2
3
    <data name="SetAttemptedValueIsInvalidAccessor" xml:space="preserve">
    <value>Value for the field {1} is invalid.</value>
  </data>


SharedResources.nl.resx
code:
1
2
3
    <data name="SetAttemptedValueIsInvalidAccessor" xml:space="preserve">
    <value>Waarde voor het veld {0} is niet geldig.</value>
  </data>

Tevens moet je ook een lege 'SharedResources.cs' class hebben, onder dezelfde naam als je resource bestand.

Het resultaat is als volgt:

https://www.imgdumper.nl/uploads9/5e1c4635dc539/5e1c4635d6c29-Schermafbeelding_2020-01-13_om_11.27.54.png

Kortom, mijn Maandag kon niet beter beginnen :)

  • DaFeliX
  • Registratie: december 2002
  • Laatst online: 11:17

DaFeliX

Tnet Devver
kevinkrs schreef op maandag 13 januari 2020 @ 11:28:
[...]

Kortom, mijn Maandag kon niet beter beginnen :)
Nu nog aangeven aan de gebruiker wat dan wel een geldige waarde zou zijn ;)

Einstein: Mijn vrouw begrijpt me niet

Pagina: 1


Apple iPhone 11 Microsoft Xbox Series X LG OLED C9 Google Pixel 4 CES 2020 Samsung Galaxy S20 4G Sony PlayStation 5 Nintendo Switch Lite

'14 '15 '16 '17 2018

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2020 Hosting door True