I am using NewtonSoft.JSON. When running
JsonConvert.SerializeObject(myObject)
it is adding an $id value to my JSON - like this:
"$id": "1",
"BookingId": 0,
"CompanyId": 0,
"IsCashBooking": false,
"PaymentMethod": 0,
"IsReferral": false,
"IsReferralPercent": false,
"ReferralPaymentType": 0,
"ReferralDues": 0,
"PassengerId": 0,
"DepartmentID": 0,
"CostCenterID": 0,
"DeadMiles": 0
Can we remove this $id with some JsonSerializerSettings or by any other method?
If yes - then how...
I added this code to my WebApiConfig register method and I got rid of all $id in JSON.
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;
If for some reason you're using a custom ContractResolver, take a look at this other stack overflow;
Json.Net adding $id to EF objects despite setting PreserveReferencesHandling to "None"
To remove the $id in JSON for my web API. I included [JsonObject(IsReference = false)] for my class objects and [JsonProperty(IsReference = false)] for my properties that are of object types. In my case the RespObj property is generic Type T and could take any object type I pass to it including collections so I had to use [JsonProperty(IsReference = false)] to get rid of the $id in the serialized JSON string.
I did not change my WebApiConfig because I was using the MVC help page for WEB API and it required me to have this configuration in my webApiconfig:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;
json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
The class object
[DataContract(IsReference = true)]
[JsonObject(IsReference = false)]
public class QMResponse<T> where T : new()
{
public QMResponse()
{
}
/// <summary>
/// The response code
/// </summary>
public string RespCode { get; set; }
/// <summary>
/// The response message
/// </summary>
public string RespMxg { get; set; }
/// <summary>
/// The exception message
/// </summary>
public string exception { get; set; }
/// <summary>
/// The object type returned
/// </summary>
[JsonProperty(IsReference = false)]
public T RespObj { get; set; }
/// <summary>
/// No of records returned
/// </summary>
public long RecordCount { get; set; }
/// <summary>
/// The Session Object
/// </summary>
public string session_id { get; set; }
}
In case 'id' is a property of your class then apply [JsonIgnore] attribute on it.
Otherwise probably here is the answer for your question:
http://james.newtonking.com/json/help/index.html?topic=html/PreserveObjectReferences.htm
You can keep the basic configuration:
Newtonsoft.Json.PreserveReferencesHandling.All;
I used this code format for my methods
public JsonResult<T> Get()
{
return Json(result);
}
It works fine for me.
The custom ContractResolver setting overrides the PreserveReferencesHandling setting.
In your implementation of DefaultContractResolver/IContractResolver, add this;
public override JsonContract ResolveContract(Type type) {
var contract = base.ResolveContract(type);
contract.IsReference = false;
return contract;
}
This behaves similarly to the PreserveReferencesHandling.None setting without a custom ContractResolver
Related
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.
I try to moq my DbContext like in memory db. I use PostgreSql in my app, so I have entities with jsonb properties. For example:
[Table("examples")]
public class Example
{
/// <summary>
/// id (autogenerated by DB)
/// </summary>
[Column("id", TypeName = "bigserial")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
/// <inheritdoc/>
[Column("layout_config", TypeName = "jsonb")]
[Required]
public LayoutConfigDto LayoutConfig { get; set; }
}
[Keyless]
public class LayoutConfigDto
{
/// <summary>
/// Координата X расположения виджета
/// </summary>
public byte X { get; set; }
/// <summary>
/// Координата Y расположения виджета
/// </summary>
public byte Y { get; set; }
}
so LayoutConfigDto just a model for JSON, that doesn't need a table. And doesn't need any relation or configuration for table.
Then I create Test class:
[TestFixture]
public class ExampleServiceTests
{
private IExampleService _exampleService;
[SetUp]
public void Setup()
{
DbContextOptions<ExampleDbContext> options = new DbContextOptionsBuilder<ExampleDbContext>()
.UseInMemoryDatabase(databaseName: "InMemoryExampleDatabase")
.Options;
ExampleDbContext dbContext = new(options);
new FakeDatabaseDataGenerator().Generate(dbContext);
Mock<ILogger<ExampleService>> mock = new();
_exampleService = new ExampleService(dbContext, mock.Object);
}
[Test]
[TestCase(0)]
[TestCase(3)]
public async Task GetExampleTest(long id)
{
ExampleModel example = await _exampleService.GetExample(id);
if (id <= 0)
{
Assert.AreEqual(example, null);
return;
}
Assert.AreNotEqual(null, example);
}
}
When I run GetExampleTest it fails on Exceptions like:
System.InvalidOperationException : Unable to determine the relationship represented by navigation 'UserWidgetModel.LayoutConfig' of type 'LayoutConfigDto'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I can't use [NotMapped] attribute on LayoutConfig field, because I need to get it from DB and with pgsql driver all works and serializes. But with in memory db it fails. How can I change my Test to make it works? Is there any other options to mock db context?
The error it returns is pretty clear; it tells you what you need to do. If you can't use the [NotMapped] attribute, you need to configure a relationship for the fields it needs
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
}
}
}
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 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;}
}