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.
Related
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);
}
}
}
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;
}
}
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.
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.
I want to perform some simple form validation in my controller.
Here's an excerpt from the controller action:
// other code
if (!string.IsNullOrEmpty(editModel.NewPassword)
&& editModel.RepeatNewPassword != editModel.NewPassword) {
// problem line... How to I get the key from editModel?
ModelState.AddModelError("", "The new password does not match the repeated password.")
}
// other code
It appears that must use a string as the error's key. Is there a way i can generate the corect key from the model, or should I just check for what input name Html.PasswordFor(x => x.NewPassword) returns?
Or in your ViewModel you can do this
public class ClassName
{
public string Password { get; set; }
[Compare("Password" , "Password Must Match")]
public string ConfirmPassword { get; set; }
}
this is new to mvc3 and you can implement your custom attribute like this fairly easy in mvc3
because IsValid now recives a ValidationContext parameter which contains information about the validation that is being performed like the type of the model and metadata associated with it so you can use reflection to get other properties and their value the CompareAttribute made use of this feature
Not exactly what you are asking for, but will solve your problem:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
{
#region [ Fields ]
/// <summary>
/// Defines the default error messsage
/// </summary>
private const string DefaultErrorMessage = "'{0}' and '{1}' do not match.";
/// <summary>
/// Defines a typeId
/// </summary>
private readonly object typeId = new object();
#endregion
#region [ Constructors ]
/// <summary>
/// Initializes a new instance of the PropertiesMustMatchAttribute class.
/// </summary>
/// <param name="originalProperty">The original property name</param>
/// <param name="confirmProperty">The confirm (or match) property name</param>
public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
: base(DefaultErrorMessage)
{
this.OriginalProperty = originalProperty;
this.ConfirmProperty = confirmProperty;
}
#endregion
#region [ Properties ]
/// <summary>
/// Gets the confirm property name
/// </summary>
public string ConfirmProperty { get; private set; }
/// <summary>
/// Gets the original property name
/// </summary>
public string OriginalProperty { get; private set; }
/// <summary>
/// Gets a unique identifier for this <see cref="T:System.Attribute"/>.
/// </summary>
/// <returns>An <see cref="T:System.Object"/> that is a unique identifier for the attribute.</returns>
/// <filterpriority>2</filterpriority>
public override object TypeId
{
get
{
return this.typeId;
}
}
#endregion
#region [ Overrides ]
/// <summary>
/// Applies formatting to an error message, based on the data field where the error occurred.
/// </summary>
/// <returns>An instance of the formatted error message.</returns>
/// <param name="name">The name to include in the formatted message.</param>
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, this.OriginalProperty, this.ConfirmProperty);
}
/// <summary>
/// Determines whether the specified value of the object is valid.
/// </summary>
/// <returns>true if the specified value is valid; otherwise, false.</returns>
/// <param name="value">The value of the object to validate. </param>
public override bool IsValid(object value)
{
var properties = TypeDescriptor.GetProperties(value);
var originalValue = properties.Find(this.OriginalProperty, true /* ignoreCase */).GetValue(value);
var confirmValue = properties.Find(this.ConfirmProperty, true /* ignoreCase */).GetValue(value);
return Equals(originalValue, confirmValue);
}
#endregion
}
And then:
[PropertiesMustMatch("NewPassword", "RepeatNewPassword ", ErrorMessage = "The new password and confirmation password do not match.")]
public class YourModel
{
public string NewPassword {get;set;}
public string RepeatNewPassword {get;set;}
}