Kiel
Kiel
CC#
Created by Kiel on 10/15/2024 in #help
Questions/concerns about memory usage in my project
No description
7 replies
CC#
Created by Kiel on 8/4/2024 in #help
✅ How to bulk compress a directory of images until each individual image is under a set size?
I have a directory of 1200 images, some of which are and some of which aren't under 256KB. I need all images that are to be compressed, resized, etc until they are under 256KB with as minimal changes as possible... I currently have Magick.NET in my project so I'd prefer to use that if possible, but I don't even know where to begin. Should I prefer compression? Resizing? Both? Something else?
3 replies
CC#
Created by Kiel on 8/3/2024 in #help
✅ Creating a collection similar to Channel/Queue that does not "remove" elements from the collection
I have a service in my bot which stores audit logs from Discord as they are relayed across the gateway. I want to store these in memory so that they can be accessed at any point, but I also want to preserve the audit logs as they come in (via a method that waits for an audit log meeting criteria). What are some options available to me? I don't think I can use Channel or (Concurrent)Queue because they both "remove" elements from themselves as I consume them...unless there's a way to not do that?
private readonly ConcurrentDictionary<Snowflake, ConcurrentDictionary<Snowflake, IAuditLog>> _auditLogs = new();

public ConcurrentDictionary<Snowflake, IAuditLog> GetAllAuditLogs(Snowflake guildId)
=> _auditLogs.GetOrAdd(guildId, _ => new ConcurrentDictionary<Snowflake, IAuditLog>());

protected override ValueTask OnAuditLogCreated(AuditLogCreatedEventArgs e)
{
var dict = _auditLogs.GetOrAdd(e.GuildId, _ => new ConcurrentDictionary<Snowflake, IAuditLog>());
dict[e.AuditLog.Id] = e.AuditLog;
return ValueTask.CompletedTask;
}

public async Task<TAuditLog?> WaitForAuditLogAsync<TAuditLog>(Snowflake guildId, Func<TAuditLog, bool> func, TimeSpan? timeout = null)
where TAuditLog : class, IAuditLog
{
// I want to wait until OnAuditLogCreated produces an audit log matching `func`, or until timeout
}
private readonly ConcurrentDictionary<Snowflake, ConcurrentDictionary<Snowflake, IAuditLog>> _auditLogs = new();

public ConcurrentDictionary<Snowflake, IAuditLog> GetAllAuditLogs(Snowflake guildId)
=> _auditLogs.GetOrAdd(guildId, _ => new ConcurrentDictionary<Snowflake, IAuditLog>());

protected override ValueTask OnAuditLogCreated(AuditLogCreatedEventArgs e)
{
var dict = _auditLogs.GetOrAdd(e.GuildId, _ => new ConcurrentDictionary<Snowflake, IAuditLog>());
dict[e.AuditLog.Id] = e.AuditLog;
return ValueTask.CompletedTask;
}

public async Task<TAuditLog?> WaitForAuditLogAsync<TAuditLog>(Snowflake guildId, Func<TAuditLog, bool> func, TimeSpan? timeout = null)
where TAuditLog : class, IAuditLog
{
// I want to wait until OnAuditLogCreated produces an audit log matching `func`, or until timeout
}
2 replies
CC#
Created by Kiel on 7/13/2024 in #help
How to move an item in a "sorted" list above another item, preserving the sorting?
This is less of a C#-specific question, but my brain is really struggling to comprehend this. I'm operating on a double-sorted collection, where items are sorted first by their Position, and then by their Id if Positions are equal. Take the following list (P=Position,I=Id):
A - P1I1
B - P2I1
C - P3I1
D - P4I1
E - P4I2
F - P5I1
G - P5I2
...imagine a similar pattern continuing
A - P1I1
B - P2I1
C - P3I1
D - P4I1
E - P4I2
F - P5I1
G - P5I2
...imagine a similar pattern continuing
Let's say I want to move C between D and E. What do I need to do to ensure that C is now sorted between D and E, given: - Id cannot be modified - Position can be modified, but cannot go below 1 - Sorting order should be preserved for all other items if possible (IE, E and above should still be in the same order as before) - Position and Id have no relation, it's just how what I am working with sorts them Sorry, I know this sounds like some stupid programming class homework, but it's a real life issue I'm dealing with my Discord bot right now with re-ordering roles and I wanted to try to simplify it as much as possible.
18 replies
CC#
Created by Kiel on 5/17/2024 in #help
Coupling attachments/files to the database in a portable way.
I understand it's not best practice to store (especially large) attachments/files in a database (PostgreSQL in my case) as it could tank performance depending on how your queries are formatted. I would like to instead maybe store a pointer to the file location on disk, but this isn't portable between devices long term. I don't really have the means or manpower to run my own CDN for a project where only the project itself needs access to the files....what should I do? One option that might maybe work is to devise and run an API for storing and retrieving attachments on the same network, but I'd be worried about testing and prod referencing the same set of files leading to potential collisions or other potential don't-touch-prod-data issues
27 replies
CC#
Created by Kiel on 5/8/2024 in #help
Concern over inheritance with static interface methods
Consider the following interface:
public interface ILuaModel
{
static abstract void SetUserDataDescriptor(DefaultUserDataDescriptorProvider provider);
}

public interface ILuaModel<TModel> : ILuaModel
where TModel : class
{
new static void SetUserDataDescriptor(DefaultUserDataDescriptorProvider provider)
=> provider.SetDescriptor<TModel>(new InstanceTypeUserDataDescriptor(typeof(TModel), namingPolicy: CamelCaseUserDataNamingPolicy.Instance));
static void ILuaModel.SetUserDataDescriptor(DefaultUserDataDescriptorProvider provider) => SetUserDataDescriptor(provider);
}
public interface ILuaModel
{
static abstract void SetUserDataDescriptor(DefaultUserDataDescriptorProvider provider);
}

public interface ILuaModel<TModel> : ILuaModel
where TModel : class
{
new static void SetUserDataDescriptor(DefaultUserDataDescriptorProvider provider)
=> provider.SetDescriptor<TModel>(new InstanceTypeUserDataDescriptor(typeof(TModel), namingPolicy: CamelCaseUserDataNamingPolicy.Instance));
static void ILuaModel.SetUserDataDescriptor(DefaultUserDataDescriptorProvider provider) => SetUserDataDescriptor(provider);
}
I have two classes, LuaUser and LuaMember. When I implement the classes like so, I get the following error:
class LuaUser : ILuaModel<LuaUser> { }

// Interface member 'ILuaModel.SetUserDataDescriptor' doesn't have a most specific implementation. Neither 'ILuaModel<LuaMember>.SetUserDataDescriptor', nor 'ILuaModel<LuaUser>.SetUserDataDescriptor' are most specific.
class LuaMember : LuaUser, ILuaModel<LuaMember> { }
class LuaUser : ILuaModel<LuaUser> { }

// Interface member 'ILuaModel.SetUserDataDescriptor' doesn't have a most specific implementation. Neither 'ILuaModel<LuaMember>.SetUserDataDescriptor', nor 'ILuaModel<LuaUser>.SetUserDataDescriptor' are most specific.
class LuaMember : LuaUser, ILuaModel<LuaMember> { }
If I add the suggested fix for this, which is creating an implementation of the non-generic SetUserDataDescriptor...Will this cause any issues? What should this method do exactly? Currently I am utilizing reflection to get all ILuaModel implementations, to call SetUserDataDescriptor, and I just want to know what I should do here to make sure both LuaUser and LuaMember have their methods called appropriately.
1 replies
CC#
Created by Kiel on 11/26/2023 in #help
More efficient or performant way of serializing permission nodes?
As a personal project, I'm designing a chat platform similar to Discord. Discord's permission system uses a simple bitfield, which is great as it's a small size and simply expressed, but terrible as I'm limited to n-1 possible permission values where n is the bit size of the field (I believe Discord internally uses a 64-bit integer, so 63 total possible permissions). I consider this bad for futureproofing and I would like to make sure I'm not painted into a corner and can have any arbitrary number of permission nodes/values. Anyone have any great ideas for alternative ways to format arbitrary permissions in a JSON-serializable-friendly way? Right now my only genius idea is still using an enum, but making it essentially an array instead of a bitfield. This would greatly increase the size of the generated JSON most likely, but now I have a massively increased number of permission values. even using just a humble uint, I'd now have billions, which is more than enough for me. IE:
"permissions": [
"CreateX", // or 0
"ModifyX", // 1
"DeleteX", // 2
"CreateY", // 3
"ModifyY", // 4
"DeleteY", // 5
// etc
]
"permissions": [
"CreateX", // or 0
"ModifyX", // 1
"DeleteX", // 2
"CreateY", // 3
"ModifyY", // 4
"DeleteY", // 5
// etc
]
strings are probably slower, and obviously take up precious bandwidth at the scale of potentially hundreds or thousands of permissions arrays, so I may opt for the less-readable but identical int value format...but I'd like to know if anyone with more experience or knowledge has a brighter idea. Maybe something silly like multiple bitfields across multiple field names?
16 replies
CC#
Created by Kiel on 11/24/2023 in #help
Real-time chat platform frameworks: SignalR, WebSockets, ???
As a project I want to task myself with to learn some new technologies, I want to develop a chat app a la Discord. I'm curious what technologies are out there that can allow clients to connect to the server and receive "events" for things like messages, etc. etc. I know of WebSockets, which are probably the most common - but I'm curious what other tools or frameworks I could utilize on the server side that can still be easily accessed by cross-platform (mobile, desktop) clients. I'm looking for backend-only systems if preferable as I will likely try to build the clients (once I get to that point) in something else entirely. I would also like to maybe do a little bit of premature overengineering and build it so that it could easily scale beyond just running it on one random computer for the entire world. I'm trying to be realistic and knowing that this is just a pet project I don't plan on trying to release it in a state to scale or compete with huge platforms, but I'd like to learn as maybe it could be something I could add to my portfolio. I have a feeling if I design it for small-scale, that upscaling it down the line will be a huge pain.
51 replies
CC#
Created by Kiel on 11/23/2023 in #help
Using reflection to build a dictionary of methods -> attributes at runtime
I'd like the following setup to work:
[Request(GET, "/something/{0}")]
public static async Task<...> GetSomethingAsync(int foo, ...)
{
var request = CreateRequest(foo);
}

private static HttpRequestMessage CreateRequest(params object[] data)
{
// I'd like to be able to access the values of the [Request] attribute in GetSomethingAsync()
// via a lookup table (dictionary) generated once at runtime, so that I don't have to use StackTrace/StackFrame
// every single time, which hurts performance greatly.
var method = GetCallingMethodInfoFromLookup();
var requestAttribute = method.GetCustomAttribute<RequestAttribute>();
return new HttpRequestMessage(requestAttribute.Method, string.Format(requestAttribute.Route, data));
}
[Request(GET, "/something/{0}")]
public static async Task<...> GetSomethingAsync(int foo, ...)
{
var request = CreateRequest(foo);
}

private static HttpRequestMessage CreateRequest(params object[] data)
{
// I'd like to be able to access the values of the [Request] attribute in GetSomethingAsync()
// via a lookup table (dictionary) generated once at runtime, so that I don't have to use StackTrace/StackFrame
// every single time, which hurts performance greatly.
var method = GetCallingMethodInfoFromLookup();
var requestAttribute = method.GetCustomAttribute<RequestAttribute>();
return new HttpRequestMessage(requestAttribute.Method, string.Format(requestAttribute.Route, data));
}
Is this possible? While trying to find out how to do this at all I found that I could use something similar to new StackFrame(1).GetMethod() to retrieve the calling method, adjusting the 1 to the level of nesting in the method, but this is 1) extra hacky IMO and 2) seems like it has serious negative performance impacts. Something I have considered is using [CallerMemberName] to make it a lookup based solely on method names, but as I've just started defining all these methods I have no clue if I will be making methods with overloads, IE ending up with two or more methods with the same name, making the lookup not possible.
3 replies
CC#
Created by Kiel on 11/12/2023 in #help
Deserializing 3rd-party JSON into a base (abstract) class in System.Text.Json
What is the most performant way to accomplish this? The most common solution to this problem (in Json.NET anyway?) is to implement a custom JsonConverter which first converts the object to a JObject, then checks for a discriminator property (or properties) before deciding how to deserialize/convert the JObject. This works, clearly, but I have doubts/concerns about its performance at scale as far as speed is concerned. I really, REALLY would like to avoid anything involving shoving every single possible field from the derived classes into one massive model abomination. Changing the JSON is out of the question as this is third-party data coming in from a websocket. The data is pretty much
{
"type": "SomeType",
...
}
{
"type": "SomeType",
...
}
followed by SomeType-specific event fields. type is guaranteed to exist. Could I use this field as a discriminator somehow? My concern is that solutions involving JsonConverter delve into solutions almost always including a switch statement on the discriminator property. My issue is that this type field can have...almost 35 values, and I'm sure even more as the events this websocket can send to me will expand with time as well. The idea of a 35-case switch statement doesn't sit well with me
4 replies
CC#
Created by Kiel on 11/11/2023 in #help
ClientWebSocket hangs on ReceiveAsync, never continues
I will concede I am brand new to working with websockets. I'm following this Establishing a connection guide which uses websockets for connection. One of the recommended ways for initiating a connection is to connect normally, and then send an Authenticate event to the server upon which you will receive either an Error or Authenticated event. In an obviously rough draft attempt to accomplish this feat:
try
{
var node = await restClient.ApiClient.QueryNodeAsync(cancellationToken);
await _ws.ConnectAsync(new Uri(node.Ws), cancellationToken);
var model = new AuthenticateSendEventApiModel(token.RawToken);

using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream) {AutoFlush = true})
{
var json = JsonConvert.SerializeObject(model);
await writer.WriteAsync(json);
await _ws.SendAsync(stream.ToArray(), cancellationToken);
}

var stream2 = await _ws.ReceiveAsync(cancellationToken); // hangs here
logger.LogInformation("Length: {Length}", stream2.Length);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed");
}
try
{
var node = await restClient.ApiClient.QueryNodeAsync(cancellationToken);
await _ws.ConnectAsync(new Uri(node.Ws), cancellationToken);
var model = new AuthenticateSendEventApiModel(token.RawToken);

using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream) {AutoFlush = true})
{
var json = JsonConvert.SerializeObject(model);
await writer.WriteAsync(json);
await _ws.SendAsync(stream.ToArray(), cancellationToken);
}

var stream2 = await _ws.ReceiveAsync(cancellationToken); // hangs here
logger.LogInformation("Length: {Length}", stream2.Length);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed");
}
my _ws is a class which wraps ClientWebSocket, and I can confirm my data is being sent (at least, no exception is thrown), but then when I try to receive, it never advances. Even stepping into it and breakpointing the actual ClientWebSocket#ReceiveAsync method, it never advances beyond this point. Is there a trick I'm missing?
29 replies
CC#
Created by Kiel on 10/23/2023 in #help
❔ Reducing Result-pattern annoyances
I know Result patterns can be a bit controversial as far as their benefits, but I'm giving them a try in my project. Anyways...I've outsourced some work to a scoped service which returns a Result<T>. My methods which call this outsourced code have this exact same structure:
var result = await service.DoSomethingAsync(...);
if (!result.IsSuccess)
return Response(result.ErrorMessage);

var obj = result.Value;
// do stuff with obj
var result = await service.DoSomethingAsync(...);
if (!result.IsSuccess)
return Response(result.ErrorMessage);

var obj = result.Value;
// do stuff with obj
Are there any ways to reduce this into a simpler form to further reduce the amount of code needed to perform the "try action, return error message if error else get success value" process? I certainly will concede I think I'm splitting hairs here and this is already simple enough for some, but I feel like I'm...missing something to make this more streamlined - maybe pattern matching could be utilized here?
9 replies
CC#
Created by Kiel on 10/22/2023 in #help
❔ Automatic discovery of scoped services without a marker interface
In the past, I had an IService interface which defined a (singleton at the time) service and a contract for certain service-level init functionality etc. This was coupled with an extension method which used reflection to find all IService implementation types and register them with my service provider at startup. I'm trying to do the same thing with scoped services, but I'm struggling to think of ANYTHING to put on this prospective IScopedService interface, and I always feel like marker interfaces are...smelly. Any suggestions? Or should I just ignore my gut feeling and be happy with a marker interface for this extremely specific usecase?
8 replies
CC#
Created by Kiel on 9/3/2023 in #help
❔ AuthZ/AuthN with ASP.NET Minimal APIs
What's the correct way to do authz/authn with minimal APIs in asp.net? I'm doing both of those things based on the provided Authorization header, which is an API token of the format:
+ Hashed ID portion Generated cryptographic portion
MQAzADIAMwA0ADkANAAzADkANQAwADYAMwA4ADIAOAA0ADgA.EFujxWr7BQRRBkiVEmJozkcGMjtIbAbOjQdQiw7GB+Q=
+ Hashed ID portion Generated cryptographic portion
MQAzADIAMwA0ADkANAAzADkANQAwADYAMwA4ADIAOAA0ADgA.EFujxWr7BQRRBkiVEmJozkcGMjtIbAbOjQdQiw7GB+Q=
AuthZ will be treating the hashed ID like a username and the cryptographic portion like an auto-generated password to validate a user's identity. AuthN will be simply validating that the (unhashed) ID matches the ID in the route the user is requesting. What's the simplest (but still correct) way to do this? the article I read went wayyy over my head and looked designed for much more complicated solutions so maybe if someone else were to explain it I'd hopefully understand. I'm unsure if it's just as simple as me implementing my own IAuthorizationService/IAuthenticationService thonk2
2 replies
CC#
Created by Kiel on 8/28/2023 in #help
❔ Avoiding duplicating code between DB models and DTOs
Hi there. I don't know if "DTO" is the right word to describe what I'm talking about, but... I have a Discord bot which is also hosting an API to allow other bots/platforms/applications to interact with the bot for some features in a TOS-abiding way. At the present moment, my DB models also have logic attached to them, which I plan to separate in some way to keep the models themselves tidy and readable. I've been told it's not a best practice to return/serve DB models directly as JSON in API calls, so I am looking to avoid doing that via, what I believe would be DTOs. How do I avoid the unnecessary code duplication in what would effectively be a copy of the model class(es) in the API? My main concern is, even if I couldn't avoid it, any changes to DB models would require me to remember to make an identical modification where appropriate to the API DTOs. This is not something I'd prefer as my current setup already suffers from that issue. Any suggestions or ideas are welcome. At the moment, I don't see a huge need for DTOs on my bot side of things as I'd be doing the data manipulation behind the scenes and Discord bots don't exactly return data like APIs do...but I do have some pretty complicated logic attached to the current models that I'd either have to extract as extension methods or DTOs...for a second time (bot DTOs / API DTOs)
15 replies
CC#
Created by Kiel on 6/16/2023 in #help
✅ JSON serializing Optional type throws an exception(?)
Newtonsoft.Json.JsonWriterException: Token PropertyName in state Property would result in an invalid JSON object. Path ''. Here's my JsonConverter:
public class OptionalJsonConverter : JsonConverter<IOptional>
{
public override void WriteJson(JsonWriter writer, IOptional? value, JsonSerializer serializer)
{
if (value is not { HasValue: true })
return;

var optionalValue = value.Value;
if (optionalValue is null)
{
writer.WriteNull();
}
else
{
serializer.Serialize(writer, optionalValue);
}
}

public override IOptional ReadJson(JsonReader reader, Type objectType, IOptional? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
// This calls a default constructor for an Optional<T> type.
return (IOptional) objectType.GetConstructors()[0].Invoke(new[] { serializer.Deserialize(reader, objectType.GenericTypeArguments[0]) });
}
}
public class OptionalJsonConverter : JsonConverter<IOptional>
{
public override void WriteJson(JsonWriter writer, IOptional? value, JsonSerializer serializer)
{
if (value is not { HasValue: true })
return;

var optionalValue = value.Value;
if (optionalValue is null)
{
writer.WriteNull();
}
else
{
serializer.Serialize(writer, optionalValue);
}
}

public override IOptional ReadJson(JsonReader reader, Type objectType, IOptional? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
// This calls a default constructor for an Optional<T> type.
return (IOptional) objectType.GetConstructors()[0].Invoke(new[] { serializer.Deserialize(reader, objectType.GenericTypeArguments[0]) });
}
}
I'm not quite sure what's going on here as it's difficult for me to debug what JSON is being serialized by the Serialize call or what Json.NET is trying to validate later on. Serializing to a StringBuilder instead of the writer yields a simple string property is being serialized, and yet this exception is being thrown. Google was very unhelpful as one of the only results for this exact error message was for something completely different, and the exception message doesn't really yield any clues. I found out about ITraceWriter, and here's what it was able to output:
2023-06-16T08:24:08.897 Info Started serializing Rivoltante.Rest.CreateMessageApiModel. Path ''.
2023-06-16T08:24:08.926 Info Started serializing Rivoltante.Core.Optional`1[System.String] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'content'.
2023-06-16T08:24:08.931 Info Finished serializing Rivoltante.Core.Optional`1[System.String] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'content'.
2023-06-16T08:24:08.932 Info Started serializing Rivoltante.Core.Optional`1[System.String[]] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'attachments'.
2023-06-16T08:24:08.932 Info Finished serializing Rivoltante.Core.Optional`1[System.String[]] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'attachments'.
2023-06-16T08:24:08.933 Error Error serializing Rivoltante.Rest.CreateMessageApiModel. Token PropertyName in state Property would result in an invalid JSON object. Path ''.
2023-06-16T08:24:08.897 Info Started serializing Rivoltante.Rest.CreateMessageApiModel. Path ''.
2023-06-16T08:24:08.926 Info Started serializing Rivoltante.Core.Optional`1[System.String] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'content'.
2023-06-16T08:24:08.931 Info Finished serializing Rivoltante.Core.Optional`1[System.String] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'content'.
2023-06-16T08:24:08.932 Info Started serializing Rivoltante.Core.Optional`1[System.String[]] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'attachments'.
2023-06-16T08:24:08.932 Info Finished serializing Rivoltante.Core.Optional`1[System.String[]] with converter Rivoltante.Rest.OptionalJsonConverter. Path 'attachments'.
2023-06-16T08:24:08.933 Error Error serializing Rivoltante.Rest.CreateMessageApiModel. Token PropertyName in state Property would result in an invalid JSON object. Path ''.
2 replies
CC#
Created by Kiel on 5/14/2023 in #help
❔ A safe scripting language for a Discord bot
Hi there. I'm looking into a system where server owners can design fully custom commands for their server without requiring their own bot, or the main bot to implement it. I've thought about perhaps utilizing Lua for this usecase, but I'm of course concerned about things like privilege escalation or finding a way out of the sandbox, resource exhaustion, and any other issues that would come with letting users effectively run their own code. Are there any solutions out there that account for this? Lua isn't the only option obviously, I just know it's a common "I want to let users script things without them needing to know the same language the [x] is written in". Some things of note I'd be looking for: - limiting execution steps or memory usage by the sandbox - being able to pass in "context" to the sandbox (IE, the channel the custom command was run in, the user executing the command, etc)
17 replies
CC#
Created by Kiel on 5/9/2023 in #help
✅ Relicensing a brand new project from LGPLv2.1 to LGPL3
I just realized something while working on my project - I have no idea how I ended up doing this, but I seem to have licensed it everywhere as LGPLv2.1 - on GitHub, on its NuGet packages, etc etc. To my understanding, it's heavily preferred to license new works under LGPLv3 instead - how should I go about doing this fix? I'm the only person who has worked on this project at all to this point, so there shouldn't theoretically be any issues with getting consent for a relicense...right? I'm not sure if I'd be safe to just...relicense it willy nilly on github by changing the LICENSE file, but...what about the packages? should I push a new (patch? major??) version with the new license? replace the original published package (if possible?) with the re-licensed one? I mean...I literally uploaded the only packages about an hour or two ago. I'm not sure if I'm just overthinking things, or if I should be nuclear and safe and start everything from scratch again - delete and recreate the repo and all that.
7 replies
CC#
Created by Kiel on 4/22/2023 in #help
✅ Options pattern - why have a separate XOptions type?
// What's the benefit of having...
services.Configure<GitHubLfsAuthenticatorOptions>(o => o.BaseAddress = "");

// instead of just...?
services.Configure<GitHubLfsAuthenticator>(o => o.BaseAddress = "");
// What's the benefit of having...
services.Configure<GitHubLfsAuthenticatorOptions>(o => o.BaseAddress = "");

// instead of just...?
services.Configure<GitHubLfsAuthenticator>(o => o.BaseAddress = "");
Assuming my "options type" has no intent of being used outside of configuring GitHubLfsAuthenticator. What I'm currently doing, completely without the Options pattern, is this via an extension method:
public static IServiceCollection AddGitHubAuthenticator(this IServiceCollection services,
string organization, string repository, string baseAddress = "https://api.github.com/")
{
services.AddSingleton(new GitHubLfsAuthenticatorOptions
{
BaseAddress = new Uri(baseAddress),
Organization = organization,
Repository = repository
});

services.AddSingleton<GitHubLfsAuthenticator>();
services.AddSingleton<ILfsAuthenticator>(static x => x.GetRequiredService<GitHubLfsAuthenticator>());
return services;
}
public static IServiceCollection AddGitHubAuthenticator(this IServiceCollection services,
string organization, string repository, string baseAddress = "https://api.github.com/")
{
services.AddSingleton(new GitHubLfsAuthenticatorOptions
{
BaseAddress = new Uri(baseAddress),
Organization = organization,
Repository = repository
});

services.AddSingleton<GitHubLfsAuthenticator>();
services.AddSingleton<ILfsAuthenticator>(static x => x.GetRequiredService<GitHubLfsAuthenticator>());
return services;
}
However, I feel like a user who wanted to do something other than this might be confused, so I thought maybe the Options pattern was the solution, but it doesn't really seem to provide any benefit over what I have now.
22 replies
CC#
Created by Kiel on 4/19/2023 in #help
❔ System.Text.Json deserializing and serializing polymorphic classes WITHOUT a discriminator
I have a base class (record) and two derived records:
public abstract record LfsResponseObject(
string Hash,
long Size);

public record LfsResponseDataObject(
string Hash,
long Size,
IReadOnlyDictionary<string, LfsResponseObjectAction> Actions,
bool? Authenticated = null) : LfsResponseObject(Hash, Size);

public record LfsResponseErrorObject(
string Hash,
long Size,
LfsObjectError Error) : LfsResponseObject(Hash, Size)
public abstract record LfsResponseObject(
string Hash,
long Size);

public record LfsResponseDataObject(
string Hash,
long Size,
IReadOnlyDictionary<string, LfsResponseObjectAction> Actions,
bool? Authenticated = null) : LfsResponseObject(Hash, Size);

public record LfsResponseErrorObject(
string Hash,
long Size,
LfsObjectError Error) : LfsResponseObject(Hash, Size)
I need to be able to serialize these in a JSON API response, and I cannot utilize discriminators because I am adhering to the git-lfs API spec. Is my only option to create a custom JsonConverter? I don't remember Newtonsoft.Json having these kinds of issues... Edit. I can't seem to close posts because @Accord is down. I've resolved this issue by going about it a different way.
2 replies