How to properly configure one service with multiple configurations? - c#

Please help me how to correctly configure one service(1 class) with several configurations and, depending on the condition, call the service with different configuration. The configurations for the service are stored in the appsettings.json file
MailConfig:
public class MailConfiguration
{
public string? DisplayName { get; set; }
public string? From { get; set; }
public string? UserName { get; set; }
public string? Password { get; set; }
public string? Host { get; set; }
public int Port { get; set; }
public bool UseSSL { get; set; }
public bool UseStartTls { get; set; }
}
MailService:
public class MailService : IMailService
{
private readonly MailConfiguration _settings;
public MailService(IOptions<MailConfiguration> settings)
{
_settings = settings.Value;
}
public async Task<bool> SendAsync(MailData mailData, CancellationToken ct = default)
{
//Implementation
}
}
IMailService:
public interface IMailService
{
Task<bool> SendAsync(MailData mailData, CancellationToken ct);
}
Configure Service:
builder.Services.Configure<MailConfiguration>builder.Configuration.GetSection(nameof(MailConfiguration)));
builder.Services.AddTransient<IMailService, MailService>();
Just an example of the configuration I want to implement:
"MailConfiguration": [
{
"DisplayName": "Client1",
"From": "testclient1#gmail.com",
"Password": "qwerty123",
"Host": "smtp.gmail.com",
"Port": 465,
"UserName": "testclient1#gmail.com",
"UseSSL": true,
"UseStartTls": true
},
{
"DisplayName": "Client2",
"From": "testclient2#gmail.com",
"Password": "qwerty123",
"Host": "smtp.gmail.com",
"Port": 465,
"UserName": "testclient2#gmail.com",
"UseSSL": true,
"UseStartTls": true
}
]
How would this be done better in terms of best practices?
I would be very grateful, I hope someone will answer my question
I had several thoughts, but they all boil down to antipattern implementations
I thought about "factory" and "decorator", but both of my implementations use the "Dictator" anti-pattern, in which the service instance is explicitly created using the new() keyword. I feel like I'm doing something wrong

Related

JSON parse error: The document root must not follow by other values

I stumbled upon this issue but i couldnt find a soultion for it. There is a post on StackOverflow with this Problem but the author created the solution himselfe and didnt share it so.
I have Json files which need to be read and then put into a List so i can Instatiate objects with these Datapoints. But I keep getting this Error. Would be very nice if you could help
using UnityEngine;
using System;
using System.IO;
using System.Collections.Generic;
[Serializable]
public class InitialOrbit
{
public string type;
public Attributes attributes;
public Relationships relationships;
}
[Serializable]
public class Attributes
{
public string epoch;
public string mAno;
public string inc;
public string frame;
public string raan;
public string sma;
public string ecc;
public string aPer;
}
[Serializable]
public class Relationships
{
public OrbitObject orbitObject;
public int id;
public Links links;
}
[Serializable]
public class OrbitObject
{
public Links links;
}
[Serializable]
public class Links
{
public string self;
public string related;
}
public class GetDepris : MonoBehaviour
{
public string jsonLibraryPath = "myPath";
List<InitialOrbit> orbits;
public void ReadOrbitFiles()
{
string path = jsonLibraryPath + "Document.json";
Debug.Log(path);
if (File.Exists(path))
{
string json = File.ReadAllText(path);
Debug.Log("json: " + json);
InitialOrbit initialOrbit = JsonUtility.FromJson<InitialOrbit>(json);
orbits.Add(initialOrbit);
}
}
void Start()
{
ReadOrbitFiles();
}
}
A Json File looks like this:
{"data":
[{"type": "initialOrbit", "attributes": {"epoch": "1965-09-14", "mAno": null, "inc": 63.44, "frame": "J2000", "raan": null, "sma": 6608000.0, "ecc": 0.011, "aPer": 64.0}, "relationships": {"object": {"links": {"self": "/api/initial-orbits/1/relationships/object", "related": "/api/initial-orbits/1/object"}}}, "id": "1", "links": {"self": "/api/initial-orbits/1"}},
{"type": "initialOrbit", "attributes": {"epoch": "1965-05-27", "mAno": null, "inc": 51.81, "frame": "J2000", "raan": null, "sma": 6651000.0, "ecc": 0.011, "aPer": 40.0}, "relationships": {"object": {"links": {"self": "/api/initial-orbits/4/relationships/object", "related": "/api/initial-orbits/4/object"}}}, "id": "4", "links": {"self": "/api/initial-orbits/4"}},
{"type": "initialOrbit", "attributes": {"epoch": "1965-05-30", "mAno": null, "inc": 51.82, "frame": "J2000", "raan": null, "sma": 6621000.0, "ecc": 0.008, "aPer": 39.0}, "relationships": {"object": {"links": {"self": "/api/initial-orbits/5/relationships/object", "related": "/api/initial-orbits/5/object"}}}, "id": "5", "links": {"self": "/api/initial-orbits/5"}},
{"type": "initialOrbit", "attributes": {"epoch": "1965-05-29", "mAno": null, "inc": 95.78, "frame": "J2000", "raan": null, "sma": 6586000.0, "ecc": 0.009, "aPer": 134.0}, "relationships": {"object": {"links": {"self": "/api/initial-orbits/6/relationships/object", "related": "/api/initial-orbits/6/object"}}}, "id": "6", "links": {"self": "/api/initial-orbits/6"}},
...
It repeads the data with other values 100 times in 1 file
you have to create one more class, and fix another, also install Newtonsoft.Json for Unity
Root root = JsonUtility.FromJson<Root>(json);
classes
using Newtonsoft.Json;
public class Root
{
public List<Data> data { get; set;}
}
public class Data
{
public string type { get; set; }
public Attributes attributes { get; set; }
public Relationships relationships { get; set; }
public string id { get; set; }
public Links links { get; set; }
}
public class OrbitObject
{
public Links links { get; set; }
}
public class Relationships
{
[JsonProperty("object")]
public OrbitObject orbitObject;
}
public class Attributes
{
public string epoch { get; set; }
public object mAno { get; set; }
public double inc { get; set; }
public string frame { get; set; }
public object raan { get; set; }
public double sma { get; set; }
public double ecc { get; set; }
public double aPer { get; set; }
}
public class Links
{
public string self { get; set; }
public string related { get; set; }
}
if you want just an InitalOrbit you can get it
InitialOrbit initialOrbit = root.data[0];
//or
InitialOrbit initialOrbit = JsonUtility.FromJson<Root>(json).data[0];
And I highly recommend you do install Newtonsoft.Json for Unity and use it instead of JsonUtility

Cognito triggers in C#

I have not found any code examples how to write Cognito trigger in C#. I am particularly interested in pre authentication trigger.
Right now I have the following Lambda function which is set as a pre authentication trigger in Cognito:
public APIGatewayProxyResponse ExampleTrigger(APIGatewayProxyRequest request, ILambdaContext context)
{
context.Logger.LogLine("trigger called");
return new APIGatewayProxyResponse
{
StatusCode = (int)HttpStatusCode.OK
};
}
However, I receive this error:
{code: "InvalidLambdaResponseException", name: "InvalidLambdaResponseException", message: "Unrecognizable lambda output"}
I think this error is caused because the type APIGatewayProxyResponse is not correct. But what is the correct type?
According to the documentation, handler should expect an object, which represents the following JSON ( including common parameters) :
{
"version": "string",
"triggerSource": "string",
"region": "string",
"userPoolId": "string",
"userName": "string",
"callerContext": {
"awsSdkVersion": "string",
"clientId": "string"
},
"request": {
"userAttributes": {
"string": "string",
. . .
},
"validationData": {
"string": "string",
. . .
},
"userNotFound": boolean
},
"response": {}
}
Also lambda handler should return the same type of object.
Since you are working with C#, probably you can use following classes to deserialize the object. So instead of both APIGatewayProxyRequest and APIGatewayProxyResponse, please use below mentioned Event Type.
public class Event
{
[JsonPropertyName("version")]
public string Version { get; set; }
[JsonPropertyName("region")]
public string Region { get; set; }
[JsonPropertyName("userPoolId")]
public string UserPoolId { get; set; }
[JsonPropertyName("userName")]
public string UserName { get; set; }
[JsonPropertyName("callerContext")]
public CallerContext CallerContext { get; set; }
[JsonPropertyName("triggerSource")]
public string TriggerSource { get; set; }
[JsonPropertyName("request")]
public Request Request { get; set; }
[JsonPropertyName("response")]
public Response Response { get; set; }
}
public class CallerContext
{
[JsonPropertyName("awsSdkVersion")]
public string AwsSdkVersion { get; set; }
[JsonPropertyName("clientId")]
public string ClientId { get; set; }
}
public class Request
{
[JsonPropertyName("userAttributes")]
public Dictionary<string, string> UserAttributes { get; set; }
[JsonPropertyName("validationData")]
public Dictionary<string, string> validationData { get; set; }
}
public class Response
{
}
Let me mention a tip for this kind of scenarios:
Write the handler as:
public dynamic ExampleTrigger(dynamic request, ILambdaContext context)
{
return request
}
Add the following environment variable for lambda.
LAMBDA_NET_SERIALIZER_DEBUG = true
Invoke the Auth flow and check the logs on CloudWatch. You can see the content of the incoming event object.

ASP.NET Core Serilog not pushing property to its custom column

I have this setup in appsettings.json for my Serilog installation
"Serilog": {
"MinimumLevel": "Information",
"Enrich": [ "LogUserName" ],
"Override": {
"Microsoft": "Critical"
},
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "Server=.\\SQLEXPRESS;Database=Apple;Trusted_Connection=True;MultipleActiveResultSets=true;,
"schemaName": "Apple",
"tableName": "EventLogs",
"columnOptionsSection": {
"customColumns": [
{
"ColumnName": "UserName",
"DataType": "nvarchar",
"DataLength": 256,
"AllowNull": true
}
]
}
}
}
]
},
I also have a custom enricher called LogUserName that is supposed to add the users username to a column called UserName in the db.
This is the enricher:
public class LogUserName
{
private readonly RequestDelegate next;
public LogUserName(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
LogContext.PushProperty("UserName", context.User.Identity.Name);
await next(context);
//return next(context);
}
}
I have added .Enrich.FromLogContext() to my Program.cs.
So far I am able to see the UserName in the property tag but I am unable to push it to the column.
EDIT:
I use this model for my logs to the database:
public class EventLogs
{
[Key]
public int Id { get; set; }
public string Level { get; set; }
public DateTime TimeStamp { get; set; }
public string UserName { get; set; }
public string Properties { get; set; }
public string LogEvent { get; set; }
public string Exception { get; set; }
public string Message { get; set; }
public string MessageTemplate { get; set; }
}
Your enricher isn't valid. You're mixing up two concepts. Adding a property to the log context is not the same as creating an actual enricher. An actual enricher should implement ILogEventEnricher, as shown in this example.
What you actually created is ASP.NET Middleware. The LogContext.PushProprety returns an IDisposable and should be wrapped in a using statement, and then anything inside the using statement block should have the log context with the additional property. Like shown in the documentation.
One way to fix this is to remove LogUserName from your enrichers configuration. Then change your middleware to this:
public class LogUserNameMiddleware
{
private readonly RequestDelegate next;
public LogUserNameMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
using(LogContext.PushProperty("UserName", context.User.Identity.Name))
{
await next(context);
}
}
}
Note you'll need tell ASP.NET Core about the Middleware, if you haven't already done so.
public static class LogUserNameMiddlewareExtensions
{
public static IApplicationBuilder UseLogUserNameMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<LogUserNameMiddleware>();
}
}
and in the Startup.Configure method, you can add the middleware to your pipeline:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseLogUserNameMiddleware();
//your other middleware
}
}
Note, you may want to clean up your middleware to handle if there isn't a logged in user, so you don't get a NullReferenceException.

C# - Newtonsoft : Generic class for Client Response

After hours of attempts and research, I am asking for your help.
I am calling a public API which returns the same structure except for the datas returned.
For examples, the REST calls which retrieve stations and districts return those two JSON answers :
Stations response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : {
"station": [{
"number": "stationId",
"name": "stationName",
"address": "stationAddress",
"state": "1",
"latitude": "stationLat",
"longitude": "stationLong",
"slotsavailable": "10",
"bikesavailable": "20",
"pos": "0",
"district": "stationDistrict",
"lastupdate": "2016-03-28T11:47:08+02:00"
}, {...}, ...]}
}
Districts response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : { "district": [{"id": "districtId", "name": "districtName"}, {...}, ...] }
}
I am using a .NET 4.5/C# solution with Newtonsoft.Json to execute the call.
I want to make the object, mapped to the client response, generic so the execution of the call will be made as follow :
var result = await client.Execute<Response<ApiResponseDistrict>>(request);
var result = await client.Execute<Response<ApiResponseStation>>(request);
My first attempt was to make a non generic call (create a full object by returned datas) which was a success.
My second attempt was to created a generic object so I made the following classes using the JsonProperty of the library Newtonsoft :
public class ApiResponse<T>
{
[JsonProperty("response")]
public Response<T> Response { get; set; }
}
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
public class ApiResponseDistrict
{
[JsonProperty("district")]
public List<District> Districts { get; set; }
}
public class District
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
At this point, when I am executing the call the object Response is valorized and also its property Status with the value waited but the property Data is never valorized (null).
My third attempt was to continue on the second attempt but using the JsonObject of the Newtonsoft library which it's given (with the same result) :
[JsonObject("district")]
public class ApiResponseDistrict
{
public List<District> Districts { get; set; }
}
As I am new to Newtonsoft, I would like to know if it is possible to use generic classes, as I am trying to do, to mapped the object returned by the call or I have to create a complete object for each "data" returned ?
Thank you for your answer and explanations or clues for me to find the answer !
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
This adds another layer between the data, so a response would look like this:
{
"Status": …,
"Data": {
"ResponseData": {
<The actual type T>
}
}
}
Instead, you want to remove that ResponseData level:
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
So for example, for the JSON above, you would have a StationResponseData class:
public class StationResponseData
{
public List<Station> Stations
{ get; set; }
}
And then you would deserialize the JSON as Response<StationResponseData>. The Station class would then contain those properties for number, name, address, etc.

C# Deserialize list in JSON

Suppose I have JSON like this, how can I model my class for deserialization?
I have no problems to model class for standard attribute like "dummy" or normal arrays, but in this case, my "links" array is a list of items with different name ("addons", "conditions", "conversion", etc.).
"dummy": "1",
"links": {
"addons": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/offers/031C9E47-4802-4248-838E-778FB1D2CC05/addons",
"method": "GET"
},
"conditions": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/offers/031C9E47-4802-4248-838E-778FB1D2CC05/conditions",
"method": "GET"
},
"conversions": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/offers/031C9E47-4802-4248-838E-778FB1D2CC05/conversions",
"method": "GET"
},
"list_prices": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/offers/031C9E47-4802-4248-838E-778FB1D2CC05/list-prices",
"method": "GET"
},
"mutual_exclusion": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/offers/031C9E47-4802-4248-838E-778FB1D2CC05/mutually-exclusive-offers",
"method": "GET"
},
"prerequisites": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/offers/031C9E47-4802-4248-838E-778FB1D2CC05/prerequisites",
"method": "GET"
},
"product": {
"href": "/16071d9f-efec-4282-a42e-a495eea76ae0/products/f245ecc8-75af-4f8e-b61f-27d8114de5f3",
"method": "GET"
}
},
Assuming you are specifically looking for the set of LinkTypes if you will, in your JSON, could you use something like the following, and execute the Deserialize on the RootObject?
Working dotNet Fiddle: https://dotnetfiddle.net/ZWSlK4
Check out the output on the Console pane on the fiddle page.
public class Link
{
public string Href { get; set; }
public string Method { get; set; }
}
public class Links
{
[JsonProperty("addons")]
public Link Addons { get; set; }
[JsonProperty("conditions")]
public Link Conditions { get; set; }
[JsonProperty("conversions")]
public Link Conversions { get; set; }
[JsonProperty("list_prices")]
public Link ListPrices { get; set; }
[JsonProperty("mutual_exclusion")]
public Link MutualExclusion { get; set; }
[JsonProperty("prerequisites")]
public Link Prerequisites { get; set; }
[JsonProperty("product")]
public Link Product { get; set; }
}
public class RootObject
{
public string dummy { get; set; }
public Links links { get; set; }
}
and then execute the Deserializer like so.
var myDummyLinksList = JsonConvert.DeserializeObject<RootObject>(jsonText);
where jsonText contains the json string you have listed in your example:
However, if you List of links objects is dynamic and the number of objects inside varies and you need to capture all of them, then you might have to write a custom Converter that inherits from the JsonConverter object. then use the answer that #mfarouk has posted.
I forked my dotNet Fiddle and implemented his solution and it works like a boss for the dynamic case!
Working dotNet Fiddle (dynamic case): https://dotnetfiddle.net/7bFcNM
Hope this helps!
the links attribute could be parsed as key, value dictionary , the class can be like
public class JSONClass
{
public string dummy { get; set; }
public Dictionary<string, Link> links;
public class Link
{
public string Href { get; set; }
public string Method { get; set; }
}
}
then de-serialized as
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<JSONClass>(JSON);

Categories

Resources