How to reference a resource file from a Model in ASP.Net Core (3.1)? - asp.net-core

So, I read all about the new localization system in ASP.Net Core (3.1) and successfully made use of the IStringLocalizer<MyController> and the IViewLocalizer<MyView>. I also could use the localization for the [DisplayName("Property description") in Models.
Below what I seem unable to do:
In good old .Net Framework I could do this:
public class Month
{
public int MonthNumber { get; set; }
public string Name
{
get
{
switch(MonthNumber)
{
case 1:
return Properties.Resources.Jan;
case 2:
return Properties.Resources.Feb;
default:
return "?";
}
}
}
But how can I do this in a Model in ASP.Net Core 3.1?

I solved it thus:
Just add the resource files following the structure you have chosen. All docs and tutorials suggest you take a folder named "Resources" as your base folder, so that is what you see here.
The link that is most probable to survive over time that explains how to use resources in an ASP.Net Core project:
Microsoft docs on Localization for ASP.Net Core
Make sure that you mark all three "Month" resx files as Public:
Visual Studio will at first complain with the message:
Custom tool PublicResXFileCodeGenerator failed to produce an output for input file 'Month.en.resx' but did not log a specific error.
Simply get rid of this error by restarting Visual Studio!
Now you can use the resources as follows:
public string Name
{
get
{
return MonthNumber switch
{
1 => Resources.Models.Month.Jan,
2 => Resources.Models.Month.Feb,
_ => "?"
};
}
}

You need to inject IStringLocalizer to the class:
public class Month
{
public int MonthNumber { get; set; }
private readonly IStringLocalizer Localizer;
public Month(IStringLocalizer localizer)
{
Localizer = localizer;
}
public string Name
{
get
{
switch(MonthNumber)
{
case 1:
return Localizer["Jan"];
case 2:
return Localizer["Feb"]
default:
return "?";
}
}
}
}
Another approach can be done by adding localized month names for the numbers in the resource file, so:
var monthName = Localizer["4"];
// result: April for English culture
// or Nisan for Turkish culture
Just for clarification;
The resource key can have three types of access modifiers:
Internal
Public
No code generation
If the key is marked with Internal or Public you can access it as you mentioned, because the compiler will auto generate a static class .cs linked to the relevant resource file with the key names as accessible properties.
But the common approach with Asp.Net Core is to work with Shared resources, and shared resource has the access modifier as No code generation; so thats mean no peoperty keys will be generated at the backend (.cs will not be generated). And in this case you have to inject the IStringLocalizer or whatever locaizer in use to the class.
So changing the key access modifier to Internal or Public can work as well, but it is not a best practice ;)

Related

builder.Configuration.GetSection always returning null value, and IOptions<T> always has default properties

I must be missing something out in .NET 6.
This same code worked perfectly fine in previous .NET core. I've found a work arround, but please I need someone to explain to me what's going wrong.
I have a settings class with its JSON representation in my appsettings.json
public class MongoSettings
{
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
public MongoSettings()
{
ConnectionString = string.Empty;
DatabaseName = string.Empty;
}
}
According to what I read in this documentation, I should add the settings this way:
builder.Services.Configure<MongoSettings>(c => builder.Configuration.GetSection("MongoSettings"));
And I did just that.
I inject this setting as an IOption inside an object I registered in my DI container.
This is the constructor that will receive the settings.
public MongoConnection(IOptions<MongoSettings> option)
The problem is that the object I receive in the settings always has default values.
My settings are present in the configuration appsettings.json I know that because, when I do the following, I get my settings with its value set to what is in my settings json file:
var mongoSettings = Configuration.GetSection("MongoSettings").Get<MongoSettings>();
My question is: Why is the IOptions object I get in my constructor always default ?
This used to work in prior .net versions and in the docs they clearly state that it is the right way.

How to keep some custom attributes when generating a proxy with svcutils?

I use the following command to generate a proxy class for a WCF service :
svcutil.exe" /out:C:\SomePath\.... /n:*,Internal.FooNameSpace
http://localhost/MyService.svc
The following class :
[ProtoContract]
[ServiceContract]
public class Foo
{
[ProtoMember(1)]
[DataMember(Order = 0)]
public string Bar { get; set; }
}
Becomes :
public partial class Foo : object, System.Runtime.Serialization.IExtensibleDataObject
{
private string BarField;
[System.Runtime.Serialization.DataMemberAttribute()]
public string Bar
{
get
{
return this.BarField;
}
set
{
this.BarField = value;
}
}
}
Is there a way to keep some specific attributes on the generated class ? (eg : ProtoMember in this case). I could off course hack the proxy but it create maintenance problems.
If you're adding that as a service reference, then nope: there's no way to retain that information - it simply isn't in the WCF endpoint.
IIRC, though, the WCF code-gen does actually come up with incremental Order values when you have multiple properties - i.e. the next property would be [System.Runtime.Serialization.DataMemberAttribute(Order = 1)], then 2 etc. So one option is to in a different file (the beauty of partial class), define (in the same namespace, etc) additional info about your type:
[ProtoContract(DataMemberOffset = 1)]
public partial class Foo { }
What this means is: when processing [DataMember], add 1 to every value - that means that you should get the required 1,2,3,4... and everything will be fine, and you haven't had to change the code.
Alternatively, you can be explicit:
[ProtoContract]
[ProtoPartialMember(1, nameof(Foo.Bar))]
[ProtoPartialMember(2, nameof(Foo.AnotherProp))]
public partial class Foo { }
This gives you a lot more flexibility to specify nuance about the properties.
As another alterative, you can configure everything at runtime:
RuntimeTypeModel.Default.Add(typeof(Foo), false)
.Add(1, nameof(Foo.Bar))
.Add(2, nameof(Foo.AnotherProp));
// or AddField to get the ValueMember that you can use to set
// fine-grained control
Finally, you can just ship the data contract dll, and tell svctil to use the types it already contains. You do this with the /reference:<file path> command-line switch, or there's a similar feature when using the UI tools (that lets you choose from the available dlls).
As a second "finally" (because one is not enough): you could describe the data instead as a .proto schema, and just tell the recipient to do the codegen locally and tell svcutil to exclude it (/excludeType: or /reference:). Note that the in progress rewrite of "protogen" does not currently include [DataContract]/[DataMember] attributes, but I could get that added today if it would be useful.

Should custom ASP.NET Core options implement IOptions<>?

When browsing the official documenation on ASP.NET Core configuration, we can find the following sample. This is also dominant in other samples out there.
public class MyOptions
{
public MyOptions()
{
// Set default value.
Option1 = "value1_from_ctor";
}
public string Option1 { get; set; }
public int Option2 { get; set; } = 5;
}
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration
I was browsing the ASP.NET Core Caching repo and noticed a slight difference in how the Microsoft team does it.
public class MemoryCacheOptions : IOptions<MemoryCacheOptions>
{
// removed stuff
MemoryCacheOptions IOptions<MemoryCacheOptions>.Value
{
get { return this; }
}
}
https://github.com/aspnet/Caching/blob/dev/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs
What is the benefit of implementing IOptions<>? What type of 'magic' does it give us?
No, it's not necessary to implement the interface. As far as I know, it does not provide any value. Most of the code in the class is rather old (2-3 years) so it might just be some leftovers from an earlier options implementation. In fact, my guess it that it can be removed safely.
Posted the question on the ASP.NET Core github and got an answer from one of the devs. By implementing IOptions<>, you can more easily use it in unit tests without having the Options infrastructure in place.

Support aliased arguments in get requests for web api

Background
I have a web api project which uses complex types for GET requests, here is an example of a controller method, and its associated complex type
[RoutePrefix("api")]
public class MyController : ApiController
{
[Route("Something")]
public IHttpActionResult GetSomething([FromUri]RequestObject request)
{
// do something to get "data"
return Ok(data);
}
}
// elsewhere
public class RequestObject
{
[Required]
public string SomeValue{get;set;}
}
This works with a url such as http://domain.com/api/Something?SomeValue=foo.
I would like to use alias' for these parameters, for which I will do some complex stuff (once I have this working) but effectively I have defined an attribute AliasAttribute.
[AttributeUsage(AttributeTargets.Property,AllowMultiple=true)]
public class AliasAttribute : Attribute
{
public string ParameterName { get; private set; }
public AliasAttribute(string parameterName)
{
this.ParameterName = parameterName;
}
}
Which I would like to adorn onto my request model like so:
// elsewhere
public class RequestObject
{
[Required,Alias("val")]
public string SomeValue{get;set;}
}
Allowing my url to shorten to http://domain.com/api/Something?val=foo. This is a contrived and simplified example, but hopefully demonstrates the background.
Problem
ModelBinding in web api has become very complex compared to Mvc model binding. I am getting twisted up between IModelBinder, IValueProvider, HttpParameterBinding et al.
I would like an example of where I should hook in to the model binding to allow me to write the value to my model from the querystring - note that I only use this aliasing behaviour when the route uses the FromUri attribute (see MyController.GetSomething above).
Question title: Support aliased arguments in get requests for web api. I think you are re-inventing a wheel here AliasAttribute , and have not given a really good reason why you don't want to use community ways of doing this.
I have done something similar with Newtonsoft.Json serializer. But if you want something ootb I'd have to google around.
public class RequestObject
{
[Required]
[JsonProperty("vla")]
public string SomeValue{get;set;}
}
Example SO that uses it: .Net NewtonSoft Json Deserialize map to a different property name
Here is a more agnostic way to do it.
[DataContract]
public class RequestObject
{
[DataMember(Name="val", IsRequired=true)]
public string SomeValue{get;set;}
}

VB.Net Merge property class from more than one web references

I have project that need to reference to some web service, just say my reference is
service1Facade and service2Facade
both of them contain class name objectA
i must load objectA from service1Facade and use it as parameter in service2Facade.
but i got error
"value of type service1Facade.objectA cannot be converted to service2Facade.objectA"
how can i convert these object ?
what i have try but still not work:
group all reference into same folder, but .NET change its name into
objectA and objectA1
I copy every property of the property inside objectA, but still not working.
The functionality that is responsible for generating proxy classes based on your WSDL specification doesn't know (and it shouldn't know) that both your services use the same underlying type for objectA, and as I mentioned, no assumptions can be made regarding this since web services are meant to be decoupled from each other (from the consumer point of view).
I'd say your best option is to have your own proxy class (let's say ServiceProxyDTO) that can be used in both service #1 and #2. Something along the lines of:
public class ServiceProxyDTO
{
// Define properties from "objectA"
public ServiceProxyDTO() { }
public ServiceProxyDTO(service1Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public ServiceProxyDTO(service2Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public static implicit operator service1Facade.ObjectA(ServiceProxyDTO dto)
{
return new service1Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator service2Facade.ObjectA(ServiceProxyDTO dto)
{
return new service2Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator ServiceProxyDTO(service1Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
public static implicit operator ServiceProxyDTO(service2Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
}
With this code you can instantiate ServiceProxyDTO and pass it as parameter to both service #1 and #2 (as well as get the return values from both of these services).
Hope this helps.