Asp.net method with generic parameter - c#

I have a generic APIResponse<T> object that wraps the result of an API call:
public class APIError
{
public string ErrorMessage { get; set; }
}
public class APIResponse<T>
{
public T Result { get; set; }
public APIError Error { get; set; }
public bool HasError
{
get { return Error != null; }
}
}
I have a method that calls an API:
public APIResponse<string> GetUserName()
{
APIResponse<string> response = new APIResponse<string>();
try
{
// make http request
response.Result = httpResponse;
}
catch
{
response.Error = new APIError { ErrorMessage = "Some error occurred" };
}
return response;
}
// I call the method like this
APIResponse<string> userNameResponse = GetUserName();
// i need to handle the apiResponse
HandleAPIResponse(userNameResponse);
I want to create a generic method that inspects the APIResponse<T> object, and throws an exception if it has an error, but i can't make it work without specifying the result type:
public void HandleAPIResponse(APIResponse<T> apiResponse)
{
if (apiResponse.HasError)
throw new Exception(apiResponse.Error.ErrorMessage);
}
Can i make a method that accepts APIResponse<T> as parameter, but without specifying the type of T?

The method definition should be as follows:
public void HandleAPIResponse<T>(APIResponse<T> apiResponse)
{
if (apiResponse.HasError)
throw new Exception(apiResponse.Error.ErrorMessage);
}

Someone correct me if I am wrong but I don't think that will be possible as that will assume an Overloaded style type and would be referencing a different type.

Related

C# Optional<TObject> as a return type?

Often i have a method where i want to return the error if something goes wrong, and instead of returning null, I want something less prone to errors at runtime and more easy to consume. Is there anything already done in .Net or maybe a nuget package?
Maybe have a constructor with optional parameters or object initializer would be enough?
This would have been the first approach but then every new Dto has to either have these Error property or inherit from a base class.
if (condition)
{
return new MyDto(null, error);
}
return new MyDto(someVariable, null);
So I've made this class to use a return type:
public class Optional<TObject> where TObject : class
{
public Optional(TObject? value)
{
Value = value;
}
public Optional(String error)
{
Error = error;
}
public TObject? Value { get; }
public String Error { get;} = String.Empty;
public Boolean IsError => !String.IsNullOrEmpty(Error);
}
I return it in the method:
if (condition)
{
return new Optional(error);
}
return new Optional(new MyDto(someVariable));
And then consume it like this:
var result = await myService.GetSomethingAsync();
if(result.IsError)
{
await DisplayAlert("error", result.Error, "Ok");
}
else
{
await DoSomethingElse(result.Value);
}
By creating a small class hierarchy, you could ensure that the Value property is only available when no error occurred
public abstract class Result
{
public virtual string Message => null;
public static Error Error(string message) => new Error(message);
public static Okay<T> Okay<T>(T value) where T : class => new Okay<T>(value);
}
public class Error : Result
{
public Error(string errorMessage) => Message = errorMessage;
override public string Message { get; }
}
public class Okay<T> : Result
where T : class
{
public Okay(T value) => Value = value;
public T Value { get; }
}
Usage
Result result = Result.Error("Something went wrong");
// OR
Result result = Result.Okay(new MyDto(someVariable));
if (result is Okay<MyDto> dtoResult) {
Console.WriteLine(dtoResult.Value);
} else {
Console.WriteLine(result.Message);
}
Or by using a recursive pattern, we can retrieve the value into a variable directly
if (result is Okay<MyDto> { Value: var dto }) {
Console.WriteLine(dto);
} else {
Console.WriteLine(result.Message);
}
Note that I have declared the Message property in the abstract base class Result, so that you don't have to cast to the Error type to get the message.
I used null as defualt value for the error message, as it allows us to write
Console.Writeline(result.Message ?? "okay");
This OneOf recommendation you got looks promising. I will personally have a look at it later.
What I do with my services is to standardize the result they return by using a SvcResult class or an inherited class.
Example:
public class SvcResult
{
public List<Error> Errors { get; } // Error is a class of my own. Add set; if deserialization is needed.
public bool Success { get; } // Add set; if deserialization is needed.
// Then parameterless constructor for a successful result.
// Then parameterized constructor to receive errors for a failed result.
}
That is the class for side-effect service calling. If The service returns data, I derive from the above to create DataSvcResult:
public class DataSvcResult<TResult> : SvcResult
{
public TResult Data { get; }
// Add constructor that receives TResult for a successful object result.
// Expose base class constructor that takes errors.
}
Basically that's what I do. But that OneOf thing, though. Looks super intersting.

Can an optional parameter be optionally required in C#?

Is there a possibility in C# to have an optional parameter be optionally required in specific situations and have it throw an error on compile time?
Let me explain with the code below as an example. The class ServiceResponse has a constructor accepting an enumeration value and an optional string. In case the enumeration value used to instantiate the class equals Error, the message becomes required. In the example code it will throw an ArgumentNullException when no message was supplied. Sadly this will only become visible on run time. However it should become visible on compile time so it warns the developer.
public class ServiceResponse
{
public ServiceResponse(ServiceResult result, string message = null)
{
Result = result;
Message = result == ServiceResult.Error ? message ?? throw new System.ArgumentNullException(nameof(message)) : message;
}
public string Message { get; }
public ServiceResult Result { get; }
}
public enum ServiceResult {
Ok,
NotFound,
Error
}
I would make the constructor private and expose the 3 static methods required to instantiate.
You can also make the message field in CreateError(message) as NotNull, and some linters will pick this up and treat as a warning.
public class ServiceResponse
{
// Change constructor to private
private ServiceResponse(ServiceResult result, string message)
{
Result = result;
Message = message;
}
public static ServiceResponse CreateOk(string message = null)
{
return new ServiceResponse(ServiceResult.OK, message);
}
public static ServiceResponse CreateNotFound(string message = null)
{
return new ServiceResponse(ServiceResult.NotFound, message);
}
public static ServiceResponse CreateError([NotNull] string message)
{
if (string.IsNullOrEmpty(message))
{
throw new ArgumentNullException(nameof(message));
}
return new ServiceResponse(ServiceResult.Error, message);
}
... Other Class Properties
}
Would static creation methods be an option?
public class ServiceResponse
{
private ServiceResponse(ServiceResult result, string message = null)
{
Result = result;
Message = message;
}
public string Message { get; }
public ServiceResult Result { get; }
public static ServiceResponse CreateInfo(ServiceResult result, string message = null)
{
return new ServiceResponse(result, message);
}
public static ServiceResponse CreateError(string message)
{
return new ServiceResponse(ServiceResult.Error, message);
}
}
This doesn't prevent passing null to CreateError, but the developer probaly won't miss the message by accident.
The OneOf library might be able to help here.
public abstract class ServiceResponse
: OneOfBase<
ServiceResponse.OkResult,
ServiceResponse.NotFoundResult,
ServiceResponse.ErrorResult>
{
public class OkResult : ServiceResponse
{
}
public class NotFoundResult : ServiceResponse
{
}
public class ErrorResult : ServiceResponse
{
public string Message { get; }
}
}
Usage A
ServiceResponse result = ...;
if (result is ServiceResponse.OkResult ok)
...;
else if(result is ServiceResponse.ErrorResult error)
...;
Usage B
ServiceResponse result = ...;
result.Match(
ok => ...,
notFound => ...,
error => ...);
You have enum with types there. Take it and change it into specific objects with same interface(base class)
public abstract class ServiceResponse
{
public ServiceResponse(ServiceResult result, string message = null)
{
Result = result;
Message = result == ServiceResult.Error ? message ?? throw new System.ArgumentNullException(nameof(message)) : message; //questionable logic
}
public string Message { get; }
public ServiceResult Result { get; }
}
public class OkServiceResponse : ServiceResponse
{
public OkServiceResponse():base(ServiceResult.Ok){}
}
public class NotFoundServiceResponse : ServiceResponse
{
public NotFoundServiceResponse(string message):base(ServiceResult.NotFound, message){}
}
public class ErrorServiceResponse : ServiceResponse
{
public ErrorServiceResponse(string message):base(ServiceResult.Error, message){}
}

Instances of abstract class cannot be created unless i write to database first

I have two functions one for write and one for read when i try to use the read function first i get this error:
An error occurred while deserializing the Message property of class DDSRecorder.MessageContainer: Instances of abstract classes cannot be created
Here is what i dont get, if i use the write first at least once then the read works fine. i dont understand what happens in the background that makes it ok to initialize abstract class if we used it once to write.
Adding the map for it didn't resolve the problem:
if (BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
{
BsonClassMap.RegisterClassMap<MessageBase>(cm =>
{
cm.AutoMap();
cm.SetIsRootClass(true);
});
}
Here is the class i am using for the mongo collection.
[BsonIgnoreExtraElements(true)]
public class MessageContainer
{
[BsonId]
public ObjectId Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
public DateTime TimeStamp { get; set; }
[BsonElement]
public string MessageType { get; set; }
public MessageBase Message { get; set; }
[BsonConstructor]
public MessageContainer()
{
}
[BsonConstructor]
public MessageContainer(MessageBase message)
{
Message = message ?? throw new ArgumentNullException(nameof(message));
TimeStamp = DateTime.UtcNow;
MessageType = message.GetType().Name;
}
[BsonConstructor]
public MessageContainer(DateTime timeStamp, string messageType, MessageBase message)
{
TimeStamp = timeStamp;
MessageType = messageType ?? throw new ArgumentNullException(nameof(messageType));
Message = message ?? throw new ArgumentNullException(nameof(message));
}
}
And the abstract class inside:
public abstract class MessageBase
{
protected MessageBase();
public MessageBase CreateCopy();
}
Example of write method:
public bool Write(MessageContainer message)
{
if (message != null && _mongoCollection != null)
{
try
{
if (!BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
{
BsonClassMap.RegisterClassMap<MessageContainer>();
BsonClassMap.RegisterClassMap<MessageBase>(cm =>
{
cm.AutoMap();
cm.SetIsRootClass(true);
});
}
_mongoCollection.InsertOne(message);
return true;
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
}
}
return false;
}
Example of read method:
public bool GetFirstAndLastMessageTime(out DateTime firstMessageTime, out DateTime lastMessageTime)
{
if (BsonClassMap.IsClassMapRegistered(typeof(MessageContainer)))
{
BsonClassMap.RegisterClassMap<MessageBase>(cm =>
{
cm.AutoMap();
cm.SetIsRootClass(true);
});
}
var filter = Builders<MessageContainer>.Filter.Empty;
var first = _mongoCollection.Find(filter).Sort(Builders<MessageContainer>.Sort.Ascending("TimeStamp")).Limit(5).ToList().First();
var last = _mongoCollection.Find(filter).Sort(Builders<MessageContainer>.Sort.Descending("TimeStamp")).Limit(5).ToList().First();
firstMessageTime = first.TimeStamp;
lastMessageTime = last.TimeStamp;
return true;
}
What am i missing for it to be able to initialize the abstract class without the need of writing first?
Well, kind of an anti-pattern here (I don't like adding dependencies from base classes to their implementations), but a quick fix would be to add
[BsonKnownTypes(typeof(MyImplementation))]
where MyImplementation is the type that implements your abstract class.
on your MessageBase class. For me this did the trick - I was able to read the data and deserialize just fine. I didn't have to add any class maps either.
While the answer provided amitla is correct, it was not something that worked for me for multiple reasons.
One is I didn't want to change the abstract class and write down all of the implementations as tags.
Another note is that I found out from the MongoDB forums that apparently the reason for it working only after write is an auto mapper and that is why it worked for me after write.
So to make work this is what I did:
public void RegisterClassMapping(List<string> messagesType)
{
// Map base class first
BsonClassMap.RegisterClassMap<MessageBase>(cm =>
{
cm.AutoMap();
cm.SetIsRootClass(true);
});
if (messagesType.Count > 0)
{
foreach (string message in messagesType) // Do look up for each message
{
Type type = GetType("NAMEOFTHENAMESPACE." + message);
if (type == null)
{
type = GetType("NAMEOFTHENAMESPACE." + message);
}
if (type != null && !BsonClassMap.IsClassMapRegistered(type))
{
BsonClassMap.LookupClassMap(type);
}
}
}
}
And this is the generic GetType I used:
private Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
return type;
}
return null;
}
I just run this once and its working for the rest of session.

MongoDB C# driver - Serialize collection to interface

Due to environment restrictions at work I'm implementing a rough Event Sourcing store in MongoDB. I'm trying to get a list of IClientEvents from Mongo like so:
var events = await _db.GetCollection<IClientEvent>("ClientEvents").FindAsync(c => c.ClientId == clientId);
I get the following exception when I run the above mentioned repository method:
Message: System.InvalidOperationException : {document}.ClientId is not supported.
The IClientEvent interface is defined as:
public interface IClientEvent
{
Guid Id { get; set; }
long TimeStamp { get; set; }
Guid ClientId { get; set; }
}
public class ClientChangedEvent : IClientEvent
{
public Guid Id { get; set; }
public long TimeStamp { get; set; }
public Guid ClientId { get; set; }
public IEnumerable<Change> Changes;
// ... other properties for the event
}
There will be many different event types stored into a single collection, all of which will implement IClientEvent. I want to just get, in a single call, all events that have occurred to a Client by clientId.
I have registered all of the concrete implementations of IClientEvent and even added a custom discriminator:
var clientEventsDiscriminator = new ClientEventsMongoDiscriminatorConvention();
BsonSerializer.RegisterDiscriminatorConvention(typeof(IClientEvent),clientEventsDiscriminator);
BsonClassMap.RegisterClassMap<ClientChangedEvent>();
BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientChangedEvent), clientEventsDiscriminator);
I have even tried registering an ImpliedImplementationInterfaceSerializer as mentioned in this SO post but it throws an exception when I register the 2nd concrete implementation that I have already registered a serializer for IClientEvent.
Not sure where to go from here. Any help is greatly appreciated!
-- EDIT for more code:
Here is the full registration code:
var clientEventsDiscriminator = new ClientEventsMongoDiscriminatorConvention();
BsonSerializer.RegisterDiscriminatorConvention(typeof(IClientEvent),clientEventsDiscriminator);
clientEventsDiscriminator.AddEventType<ClientChangedEvent>();
BsonClassMap.RegisterClassMap<ClientChangedEvent>();
BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientChangedEvent), clientEventsDiscriminator);
clientEventsDiscriminator.AddEventType<ClientAddedEvent>();
BsonClassMap.RegisterClassMap<ClientAddedEvent>();
BsonSerializer.RegisterDiscriminatorConvention(typeof(ClientAddedEvent), clientEventsDiscriminator);
Here is the Discriminator:
public class ClientEventsMongoDiscriminatorConvention : IDiscriminatorConvention
{
private Dictionary<string, Type> _eventTypes = new Dictionary<string, Type>();
public string ElementName => "_eventType";
public BsonValue GetDiscriminator(Type nominalType, Type actualType)
{
return GetDiscriminatorValueForEventType(actualType);
}
public Type GetActualType(IBsonReader bsonReader, Type nominalType)
{
var bookmark = bsonReader.GetBookmark();
bsonReader.ReadStartDocument();
if (!bsonReader.FindElement(ElementName))
{
throw new InvalidCastException($"Unable to find property '{ElementName}' in document. Cannot map to an EventType.");
}
var value = bsonReader.ReadString();
bsonReader.ReturnToBookmark(bookmark);
if (_eventTypes.TryGetValue(value, out var type))
{
return type;
}
throw new InvalidCastException($"The type '{value}' has not been registered with the '{nameof(ClientEventsMongoDiscriminatorConvention)}'.");
}
private string GetDiscriminatorValueForEventType(Type type)
{
var indexOfEventWord = type.Name.IndexOf("Event");
if (indexOfEventWord == -1)
{
return type.Name;
}
return type.Name.Substring(0, indexOfEventWord);
}
public void AddEventType<T>()
{
var discriminatorName = GetDiscriminatorValueForEventType(typeof(T));
_eventTypes.TryAdd(discriminatorName, typeof(T));
}
}
When running the code it doesn't appear to ever hit the GetActualType method of the discriminator.
I managed to get it to work by simply changing IClientEvent from an interface to an abstract class.

How to correctly use generics and type constraints in this scenario?

I am confused on generics and type constraints, I will get straight to the point.
I have a BaseQueryResult class
public abstract class BaseQueryResult<T>
{
public int Count => Models != null && Models.Any() ? Models.Count : 0;
public Exception Exception { get; set; }
public bool HasException => Exception != null;
public bool IsSuccess => Exception == null;
public bool NotFound { get; set; }
public string ContinuationToken { get; set; }
public IList<T> Models { get; set; }
}
A child class which inherits from the above
public class TransportListingQueryResult : BaseQueryResult<TransportListingQueryModel>
{
public TransportListingQueryResult(IList<TransportListingQueryModel> models, string continuationToken)
{
Models = models;
ContinuationToken = continuationToken;
NotFound = false;
}
public TransportListingQueryResult(Exception exception)
{
NotFound = false;
Exception = exception;
}
public TransportListingQueryResult(bool notFound, Exception exception)
{
NotFound = notFound;
Exception = exception;
}
public static TransportListingQueryResult NotFoundResult(Exception exception)
{
return new TransportListingQueryResult(true, exception);
}
}
My Extension method that I am using
public static class TransportListingQueryResultExtension
{
public static IActionResult ToActionResult<T>(this T result, ControllerBase controller, int limit, string routeName, object values, HttpStatusCode successStatusCode = HttpStatusCode.OK)
where T : BaseQueryResult<T>
{
if (result.NotFound)
{
return controller.NotFound();
}
if (!result.IsSuccess)
{
if (result.HasException)
{
throw result.Exception;
}
return controller.BadRequest(new ErrorResponse { Messages = new[] { ErrorMessages.InternalServer } });
}
var uri = controller.Url.Link(routeName, values);
var response = new HyperMediaResponse<T>(
new LinkItem(uri),
new PageItem(limit, result.ContinuationToken, result.Count),
result.Models);
switch (successStatusCode)
{
case HttpStatusCode.Created:
return controller.Created(string.Empty, response);
case HttpStatusCode.OK:
default:
return controller.Ok(response);
}
}
}
And Finally my Action in my Controller
public async Task<IActionResult> Create([FromBody]CreateListingModel createListing)
{
var result = await _transportListingStore.CreateNewAsync(createListing);
return result.ToActionResult<TransportListingQueryResult>(this, 1, Constants.RouteNames.CreateListing, null, HttpStatusCode.Created);
}
I am getting an error on this line in my action
return result.ToActionResult
(
this,
1,
Constants.RouteNames.CreateListing,
null,
HttpStatusCode.Created
);
The error is:
The type 'TransportListings.API.Application.Response.TransportListingQueryResult' cannot be used as type parameter 'T' in the generic type or method 'TransportListingQueryResultExtension.ToActionResult(T, ControllerBase, int, string, object, HttpStatusCode)'. There is no implicit reference conversion from 'TransportListings.API.Application.Response.TransportListingQueryResult' to 'TransportListings.API.Application.Response.BaseQueryResult'
I am confused as my child class inherits BaseQueryResult, but the error is telling me there is no implicit reference conversion. I am not too sure what is wrong with my code and why its giving me the error it is.
Any help would be appreciated. Thanks in advance.
The problem is your constraint is impossible to satisfy:
public static IActionResult ToActionResult<T>(this T result, /* snip */)
where T : BaseQueryResult<T>
Where what you really want is something like:
public static IActionResult ToActionResult<T, U>(this T result, /* snip */)
where T : BaseQueryResult<U>
And call it like this:
return result.ToActionResult<TransportListingQueryResult, TransportListingQueryModel>(
this, /* snip */);

Categories