Is there a way to add multiple entity sets to a single controller with OData endpoints? - asp.net-core-webapi

Is there a way to have multiple OData endpoints/functions return different types mapped to a single ODataController? The normal crud operations map to an endpoint odata/foo but if I wanted to add more methods to the controller can I define a function that returns a different type under odata/foo/foosummaries?
Could I add more entity sets that would map to the same controller class? For example if I wanted to have an endpoint under my FooController that returned FooSummaries that had the endpoint ~/odata/Foo/FooSummaries?
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix)
{
builder.EntitySet<FooResponse>("Foos");
builder.EntityType<FooSummaries>()
.Function(nameof(SubmissionsController.GetFooSummaries))
.ReturnsCollection<IEnumerable<FooSummaryREsponse>>();
}
Would I need to decorate the OData controller differently with ODataPrefix or ODataRoute?
public class SubmissionsController : ODataController
{
#region Private Fields
private ISubmissionManager _manager;
private ILogger _logger;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="manager">A SubmissionManager instance.</param>
/// <param name="logger">A Logger instance.</param>
public SubmissionsController(ISubmissionManager manager, ILogger logger)
{
_manager = manager;
_logger = logger;
}
#endregion
#region HttpGet Endpoints
/// <summary>
/// Odata get all foos
/// </summary>
/// <returns></returns>
[HttpGet()]
[Produces("application/json")]
[ProducesResponseType(200, Type = typeof(IQueryable<FooResponse>))]
[ProducesResponseType(400, Type = typeof(ProblemDetails))]
[ProducesResponseType(404, Type = typeof(ProblemDetails))]
[ProducesResponseType(500, Type = typeof(ProblemDetails))]
[EnableQuery(PageSize = 20)]
public IActionResult Get()
{
try
{
IQueryable<FooResponse> result = _manager.GetFoos();
return Ok(result);
}
catch (FooException ex)
{
return ex.GetObjectResult(this);
}
}
/// <summary>
/// Get an odata queryable foo summary
/// </summary>
/// <param name="query">The query arguments</param>
/// <returns></returns>
[HttpGet("api/v{version:apiVersion}/odata/Foos/summaries")]
[Produces("application/json")]
[ProducesResponseType(200, Type = typeof(IEnumerable<FooSummaryResponse>))]
[ProducesResponseType(400, Type = typeof(ProblemDetails))]
[ProducesResponseType(404, Type = typeof(ProblemDetails))]
[ProducesResponseType(500, Type = typeof(ProblemDetails))]
[EnableQuery()]
public IActionResult GetFooSummaries()
{
try
{
IQueryable<FooSummaryResponse> results = _manager.GetFooSummaryResponse();
results);
return Ok(results);
}
catch (FooException ex)
{
return ex.GetObjectResult(this);
}
}
}

Related

Protobuf-net v3 DateTimeOffset Surrogate

I've seen some other SO's where people are using DateTimeOffset surrogates to handle deserializing those properties, however when I try to copy those, I continue to get a System.InvalidOperationException: No serializer defined for type: System.DateTimeOffset error.
[ProtoContract]
public TestClass
{
[ProtoMember(1)]
public DateTimeOffset Time { get; set; }
}
Surrogate class
[ProtoContract]
public class DateTimeOffsetSurrogate
{
[ProtoMember(1)]
public long DateTimeTicks { get; set; }
[ProtoMember(2)]
public short OffsetMinutes { get; set; }
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
{
return new DateTimeOffsetSurrogate
{
DateTimeTicks = value.Ticks,
OffsetMinutes = (short)value.Offset.TotalMinutes
};
}
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
{
return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes));
}
}
Then I'm registering it right before the http call. I've tried moving this registration into a few different places but it doesn't seem to make a difference. Did this change in v3 or something or am I doing something wrong? Sorry - new to protobuf-net :)
public async Task<Response<IEnumerable<TestClass>>> GetData()
{
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
var request = new HttpRequestMessage(HttpMethod.Get, "my-url");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
var result = await _httpClient.SendAsync(request);
var items= ProtoBuf.Serializer.Deserialize<Response<IEnumerable<TestClass>>>(await result.Content.ReadAsStreamAsync());
return items;
}
I am using version 3.1.22 (Currently the latest) of protobuf-net right now with the following setup.
// Needs to be only called at application startup, e.g. in the Startup.cs calls of your web API.
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset?), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
The surrogate handler I use successfully, can be found below. No black magic happening here, just serializing / deserializing the unix timestamp to long and vice versa:
namespace Something
{
using System;
using ProtoBuf;
/// <summary>
/// A surrogate handler for the <see cref="DateTimeOffset"/> class.
/// </summary>
[ProtoContract(Name = nameof(DateTimeOffset))]
public class DateTimeOffsetSurrogate
{
/// <summary>
/// Gets or sets the value.
/// </summary>
[ProtoMember(1)]
public long? Value { get; set; }
/// <summary>
/// Converts the <see cref="DateTimeOffsetSurrogate"/> to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="surrogate">The surrogate handler.</param>
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate surrogate)
{
if (surrogate?.Value is null)
{
throw new ArgumentNullException(nameof(surrogate));
}
var dt = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
dt = dt.ToLocalTime();
return dt;
}
/// <summary>
/// Converts the <see cref="DateTimeOffsetSurrogate"/> to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="surrogate">The surrogate handler.</param>
public static implicit operator DateTimeOffset?(DateTimeOffsetSurrogate? surrogate)
{
if (surrogate?.Value is null)
{
return null;
}
var dt = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
dt = dt.ToLocalTime();
return dt;
}
/// <summary>
/// Converts the <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffsetSurrogate"/>.
/// </summary>
/// <param name="source">The source.</param>
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset source)
{
return new DateTimeOffsetSurrogate
{
Value = source.ToUnixTimeMilliseconds()
};
}
/// <summary>
/// Converts the <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffsetSurrogate"/>.
/// </summary>
/// <param name="source">The source.</param>
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset? source)
{
return new DateTimeOffsetSurrogate
{
Value = source?.ToUnixTimeMilliseconds()
};
}
}
}
Maybe, you want to give it a try. Maybe [ProtoContract(Name = nameof(DateTimeOffset))] is what you're missing, but I'm not sure.

When do you need a parameterless constructor on a Command/DTO object for an API call?

I am getting the following exception thrown: "System.NotSupportedException: Deserialization of reference
types without parameterless constructor is not supported."
It is JSON being passed into the controller.
When do you need a parameterless constructor vs. one with parameters?
I am using ASP.NetCore.App.Ref 3.1.3 and NetCore.App.Ref 3.1.0.
Here is the class that it is saying needs a parameterless constructor:
public class JobApplicationStatusModel : BaseEntityModel
{
/// <summary>
///JobApplicationStatus ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// Description/Name of Status.
/// </summary>
public string StatusDescription { get; set; }
/// <summary>
/// Main constructor
/// </summary>
/// <param name="id">JobApplication StatusID</param>
/// <param name="statusDescription">Status Description</param>
public JobApplicationStatusModel(int id, string statusDescription)
{
Id = id;
StatusDescription = statusDescription ?? throw new ArgumentNullException(nameof(statusDescription));
}
/// <summary>
/// Returns an enumeration of all atomic values.
/// </summary>
/// <returns>An enumeration of all atomic values.</returns>
protected override IEnumerable<object> GetAtomicValues()
{
// Using a yield return statement to return each element one at a time
yield return Id;
yield return StatusDescription;
}
}

When I query my REST service with Breeze JS, the parameter is not passed

I am working on a SPA with AngularJS and BreezeJS. The backend is implemented on ASP.NET Core MVC 6 and EF 6.1.3.
The REST service is running, but when I query the service with Breeze JS, the parameter is not passed.
E.g.
[HttpGet]
[EnableQuery]
public IQueryable<Event> Events()
{
return this._contextProvider.Context.Events;
}
I tried to call the service over the browser .../Context/Events?$filter=(Id%20eq%202). The result is still the same the filter is not apply.
What have I missed? How can I add OData to MVC6?
My Controller:
namespace FleetManagement.WebApi.Controllers
{
/// <summary>Main REST / Breeze controller for the Fleet Management module.</summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.Controller"/>
[BreezeController]
[EnableCors("AllowAll")]
[EnableQuery]
[EnableBreezeQuery]
public class ContextController : Controller
{
#region Fields.
private readonly D4GEFContextProvider<FleetManagementContext> _contextProvider;
#endregion
#region Constructors / Desctructor.
/// <summary>Initializes a new instance of the <see cref="ContextController"/> class.</summary>
public ContextController()
{
var connectionString = "Data Source=lomoofsv14\\d4g;Initial Catalog=Test_Westfalen_D4G_Custom_RandomData;Integrated Security=True;MultipleActiveResultSets=True;Application Name=FMREST";
this._contextProvider = new D4GEFContextProvider<FleetManagementContext>(connectionString);
}
#endregion
#region Public Methods.
/// <summary>Get a list of assets to a given asset filter and data profile.</summary>
/// <param name="filters">The filters for the asset types is a string of type names e.g. 'Tractor, Trailer'.</param>
/// <param name="profileUId">The profile identifier for the data filter.</param>
/// <returns>IEnumerable<Asset>.</returns>
[HttpGet]
public IQueryable<Asset> Assets(string filters, string profileUId)
{
return this._contextProvider.Context.GetAssets(filters, profileUId);
}
/// <summary>Assets the types.</summary>
/// <returns>IQueryable<System.String>.</returns>
[HttpGet]
public IQueryable<string> AssetTypes()
{
return Enum.GetNames(typeof(AssetTypes))
.AsQueryable();
}
/// <summary>Data collection "Events".</summary>
/// <returns>IQueryable<Event>.</returns>
[HttpGet]
public IQueryable<Event> Events()
{
return this._contextProvider.Context.Events;
}
/// <summary>Metadata of the EF DBContext necessary for Breeze to build the JSON objects.</summary>
/// <returns>System.String.</returns>
[HttpGet]
public string Metadata()
{
return this._contextProvider.Metadata();
}
/// <summary>Saves all changes to the database.</summary>
/// <param name="saveBundle">The save bundle.</param>
/// <returns>SaveResult.</returns>
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
this._contextProvider.BeforeSaveEntityDelegate = this.BeforeSaveEntityDelegate;
this._contextProvider.AfterSaveEntitiesDelegate = this.AfterSaveEntitiesDelegate;
return this._contextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<Tractor> Tractors()
{
return this._contextProvider.Context.Tractors;
}
[HttpGet]
public IQueryable<Trailer> Trailers()
{
return this._contextProvider.Context.Trailers;
}
#endregion
#region Private Methods.
private void AfterSaveEntitiesDelegate(Dictionary<Type, List<EntityInfo>> saveMap, List<KeyMapping> keyMappings)
{
// TractorToEvent Assign Event to tractor
}
}
}

Web APIs methods with Warnings

I have this method in an API that does POST for creating a record, but before inserting that record in DB there are some validations that i must do, I might come back with warnings and I need to return back these warnings to the client to confirm back.
what is the best way to do that in Web API's? or should i split the method into 2, one for validation and one for saving?
Set up your Web Api the following way.
[HttpPost]
public IHttpActionResult DoStuff(object item)
{
if(!validate(item))
{
return this.BadRequest("Validation failed blablabla");
}
else
{
//insert logic
}
return this.Ok();
}
What happens is you validate the object you send to the API. When it fails to validate you return the request was not correct, with the message you specified.
When the validation succeeds the insert logic is called and you return a OK result when it succeeds.
I create and use a wrapper which would contain object to be bound to ui, messages (error or validation or warning) and total.
/// <summary>
/// Class for the Repository response object
/// </summary>
/// <typeparam name="T"></typeparam>
public class CLSResponse<T>
{
/// <summary>
/// Gets or sets the messages to be returned in the response.
/// </summary>
/// <value>The messages.</value>
public IEnumerable<KeyValuePair<string, string>> Messages {
get { return m_Messages; }
set { m_Messages = value; }
}
private IEnumerable<KeyValuePair<string, string>> m_Messages;
/// <summary>
/// Gets or sets the service model to be returned in the response.
/// </summary>
/// <value>The service model.</value>
public T ServiceModel {
get { return m_ServiceModel; }
set { m_ServiceModel = value; }
}
private T m_ServiceModel;
/// <summary>
/// Gets or sets the totalitems.
/// </summary>
/// <value>The TotalItems.</value>
public int TotalItems {
get { return m_TotalItems; }
set { m_TotalItems = value; }
}
private int m_TotalItems;
/// <summary>
/// Gets and Sets the Message Type based on the MessageType Struct
/// </summary>
public string MessagesType;
}
/// <summary>
/// Struct for MessageTypes to be returned with messages, in the response object
/// </summary>
public struct MessagesType
{
/// <summary>
/// Validation
/// </summary>
public static string Validation = "Validation";
/// <summary>
/// Warning
/// </summary>
public static string Warning = "Warning";
/// <summary>
/// Error
/// </summary>
public static string Error = "Error";
/// <summary>
/// Unauthorized
/// </summary>
public static string UnAuthorized = "Unauthorized";
}
Then in your repository layer or logic layer
public CLSResponse<bool> CreateUser(string request)
{
var messages = new List<KeyValuePair<string, string>>();
try
{
//Do something
if (!validation)
{
messages.Add(MessagesType.Validation, "Invalid");
return new CLSResponse<bool> {
ServiceModel = false,
Messages = messages,
MessagesType = MessagesType.Validation
};
}
else {
return new CLSResponse<bool> {
ServiceModel = true,
Messages = messages,
MessagesType = MessagesType.Error
};
}
}
catch (Exception ex)
{
messages.Add(MessagesType.Error, "UpdateFailed");
return new CLSResponse<bool> {
ServiceModel = false,
Messages = messages,
MessagesType = MessagesType.Error
};
}
}
Now on controller,
[HttpPost]
public HttpResponseMessage<CLSResponse<bool>> CreateUser(string input)
{
var res = LogicLayer.CreateUser(input);
//Check for res.MessageType and set the status code
if (res.MessagesType = MessagesType.Validation)
{
return Request.CreateResponse<CLSResponse<bool>>(HttpStatusCode.PreconditionFailed, res);
}
}
This way your response object still has the warning you added in logic layer and depending on the status code returned, you can handle those messages on client side.

dynamic load recive activity wf

I'm trying to load and invoke activityes from custom activity as follows:
Imagine I have a xamlx like this:
--Sequence
|----- LoadActiviy
|--Initialize dictionary with input data of activitity
|----- Invoke
This works when activity NOT CONTAINS receive/send messages. But when i try with activity wich contains receive/send messages the result is a exception
WorkflowApplicationUnhandledExceptionEventArgs: Only registered bookmark scopes can be used for creating scoped bookmarks.
The code:
1-Load xaml: (Load activity)
public sealed class LoadActivity : CodeActivity<Activity>
{
#region Properties
/// <summary>
/// Gets or sets Path.
/// </summary>
[RequiredArgument]
public InArgument<string> Path { get; set; }
#endregion
#region Methods
/// <summary>
/// The execute method.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <returns>
/// An activity loaded from a file
/// </returns>
protected override Activity Execute(CodeActivityContext context)
{
return ActivityXamlServices.Load(this.Path.Get(context));
}
#endregion
}
2- Run activity:
public class SynchronousSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}
public sealed class Invoke : CodeActivity
{
#region Properties
/// <summary>
/// Gets or sets Activity.
/// </summary>
/// <remarks>
/// The activity that will be invoked. Can be loaded from XAML.
/// </remarks>
[RequiredArgument]
public InArgument<Activity> Activity { get; set; }
public OutArgument<IDictionary<string, object>> Output { get; set; }
/// <summary>
/// Gets or sets Input.
/// </summary>
/// <remarks>
/// The input arguments you want to pass to the other workflow
/// </remarks>
public InArgument<IDictionary<string, object>> Input { get; set; }
#endregion
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
try
{
IDictionary<string,object> _input= this.Input.Get(context);
foreach (KeyValuePair<string,object> item in _input )
{
Debug.WriteLine(string.Format("{0} {1}", item.Key, item.Value));
}
// AutoResetEvent idleEvent = new AutoResetEvent(false);
WorkflowApplication app = new WorkflowApplication(this.Activity.Get(context),this.Input.Get(context));
app.SynchronizationContext = new SynchronousSynchronizationContext();
app.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
// idleEvent.Set();
};
app.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
e.InstanceId, e.UnhandledException.Message);
Console.WriteLine("ExceptionSource: {0} - {1}",
e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
app.Idle = e => Console.WriteLine("WorkflowApplication.Idle called");
Console.WriteLine("Before WorkflowApplication.Run()");
app.Run();
}
catch
{
throw;
}
}
}
Any ideas?
You can only use a Receive activity in a workflow hosted in a WorkflowServiceHost. Even if your main workflow is hosted in a WorkflowServiceHost the child workflow is hosted in a WorkflowApplication and can't contain a Receive activity because it isn't running as part of the WCF infrastructure.

Resources