Annotated Ignore field get's ignored in servicestack reponse - c#

I have a request like this:
ARequest : QueryBase<Person, Result>, IJoin<Person, OtherThing>
Person has the following field
[Ignore]
Public string Label { get { return FirstName + LastName; }
In my Result i have the following
public int Id;
public string Label
However, if i add an Ignore attribute to a field it gets ignored. So whenever i execute everything the only thing returned is a list of id's and in the QueryResponse the Label is always empty, if i however return a Person instead of Result i get a completely filled response.
So the question is, how do i make sure OrmLite does not search for label in the database, but sets the label in my custom return object.

After mythz explained to me the fact that if it doesn't map to your ormlite db it won't map to your resulting DTO later, i build a quick work around. Instead of returning my own response dto immediately in the following line:
ARequest : QueryBase<Person, Result>, IJoin<Person, OtherThing>
I just returned the person object:
ARequest : QueryBase<Person>, IJoin<Person, OtherThing>
Then in my service i wrote a simple mapper along the following lines:
QueryResponse<Result> response = result.ConvertTo<QueryResponse<Result>>();
response.Results = new List<Result>();
foreach (Person p in result.Results)
{
response.Results.Add(new Result{ Id = p.EmployeeId, Label = (p.FirstName + " " + p.LastName) });
}
return response
This way i made sure the label got filled with the firstname and the lastname, and yet did not have to redesign my result DTO so i could keep it very generic.

Not sure if I understand what you're trying to do, but if you're only looking to return Label you should be able to add it to the returned DTO, e.g:
public class Result
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Label { get { return FirstName + LastName } }
}

Related

Why can't I deserialize string Into a model?

I'm using a HTTP client to get a string and picking out my json from that and converting back to a string to deserialize it into a List of "Spots" but can't get it to to work
I've tried changing the DeserializeObject type to every mix of "List, IList, HardwareUpdateSpot, HardWareModel" and still it didn't work
public async Task<IList<HardwareUpdateSpot>> UpdateSpotHTTP()
{
var client = new HttpClient();
var response = await client.GetAsync(
"https://io.adafruit.com/api/v2/Corey673/feeds/673d855c-9f66-4e49-8b2c-737e829d880c");
var responseHTTP = response.Content.ReadAsStringAsync();
var j = JObject.Parse(responseHTTP.Result);
var b = j.GetValue("last_value");
var h = b.ToString();
var dataObjects = JsonConvert.DeserializeObject<IList<HardwareUpdateSpot>>(h);
return null;
}
public record HardWareModel
{
public int SpotId { get; set; }
public string Occupied { get; set; }
}
public class HardwareUpdateSpot
{
public IList<HardWareModel> Spots { get; set; }
public HardwareUpdateSpot(IList<HardWareModel> spots)
{
Spots = spots;
}
}
While trying to reproduce your problem I have examined the returned value from the API call. This is the json returned:
{"Spot":[
{"SpotId":"1","Occupied":"false",},
{"SpotId":"2","Occupied":"false",},
{"SpotId":"3","Occupied":"false",},
{"SpotId":"4","Occupied":"false"}
]}
So, it easy to see that the returned json requires a root object with a public Spot property (not Spots) and this property should be a collection.
Instead the code above expects a json that has at the root level a collection of HardwareUpdateSpot and of course it cannot work.
To fix the problem you need to change the deserialization to:
JsonConvert.DeserializeObject<HardwareUpdateSpot>(h);
Now, you need to make some changes to the HardwareUpdateSpot class to make it compatible with the json.
First you need to add a parameterless constructor required by jsonconvert, then you need to fix the difference between the name for the property (Spots) and the name returned (Spot).
So you can change the property name to match the json or add the attribute that make Spots=Spot
[JsonProperty("Spot")]
public IList<HardWareModel> Spots { get; set; }

.NET Core Razor event after Model Binding for modifications?

What I'd like to achive is to be able to modiy certain (string) values after they were binded to a property but they are being validated in .NET Core 3.1.
Example poco class:
public class MyPoco
{
[TrimContent]
[MinLength(2)]
public string? FirstName { get; set; }
[TrimContent]
[MinLength(2)]
public string? Surname { get; set; }
[TrimContent]
[LowerCase]
public string? EmailAddress { get; set; }
}
So let's say a form is posted to the MVC controller and the values entered are
" F " for the first name and " S " as the surname, " My.Email#Address.Com ".
They should be modified, i.e. trimmed to "F" and "S" and the MinLength=2 should be alerted i.e. Also: I can avoid all the Trim() statements in my code.
My idea is, that when using a "TrimContentAttribute" (and other attributes that "correct" the values in some way), all values that have been set by previous BindingSourceValueProviders and then are being processed, but before the validation kicks in.
Also attributes marked with LowerCase, should automatically be "ToLower()", so the email address would be "my.email#address.com".
So it the idea would be to declrative approch other than having all the Trim() und ToLowerCase() methods all over the code where the entity is used.
The only idea I came up with so far to write a custom source as described in
Model Binding in ASP.NET Core - Additional sources. But I actually would like to rely on all the default values providers.
Note: There are validators on client side in action as well, but I'd like to have a solution also on the server side.
a new attribute can be created
public class MinLengthWithTrim : MinLengthAttribute
{
public MinLengthWithTrim(int length) : base(length)
{
}
public override bool IsValid(object? value)
{
var str = value as string;
if (str == null)
{
return false;
}
return base.IsValid(str.Trim());
}
}
Using:
[MinLengthWithTrim(10)]
public string Name { get; set; }

How to save/pass MongoDB UpdateDefinition for logging and later use?

I am stumped on how to save/pass MongoDB UpdateDefinition for logging and later use
I have created general functions for MongoDB in Azure use on a collection for get, insert, delete, update that work well.
The purpose is to be able to have a standard, pre-configured way to interact with the collection. For update especially, the goal is to be able to flexibly pass in an appropriate UpdateDefinition where that business logic is done elsewhere and passed in.
I can create/update/set/combine the UpdateDefinition itself, but when i try to log it by serializing it, it shows null:
JsonConvert.SerializeObject(updateDef)
When I try to log it, save it to another a class or pass it to another function it displays null:
public class Account
{
[BsonElement("AccountId")]
public int AccountId { get; set; }
[BsonElement("Email")]
public string Email { get; set; }
}
var updateBuilder = Builders<Account>.Update;
var updates = new List<UpdateDefinition<Account>>();
//just using one update here for brevity - purpose is there could be 1:many depending on fields updated
updates.Add(updateBuilder.Set(a => a.Email, email));
//Once all the logic and field update determinations are made
var updateDef = updateBuilder.Combine(updates);
//The updateDef does not serialize to string, it displays null when logging.
_logger.LogInformation("{0} - Update Definition: {1}", actionName, JsonConvert.SerializeObject(updateDef));
//Class Created for passing the Account Update Information for Use by update function
public class AccountUpdateInfo
{
[BsonElement("AccountId")]
public int AccountId { get; set; }
[BsonElement("Update")]
public UpdateDefinition<Account> UpdateDef { get; set; }
}
var acct = new AccountUpdateInfo();
acctInfo.UpdateDef = updateDef
//This also logs a null value for the Update Definition field when the class is serialized.
_logger.LogInformation("{0} - AccountUpdateInfo: {1}", actionName, JsonConvert.SerializeObject(acct));
Any thoughts or ideas on what is happening? I am stumped on why I cannot serialize for logging or pass the value in a class around like I would expect
give this a try:
var json = updateDef.Render(
BsonSerializer.SerializerRegistry.GetSerializer<Account>(),
BsonSerializer.SerializerRegistry)
.AsBsonDocument
.ToString();
and to turn a json string back to an update definition (using implicit operator), you can do:
UpdateDefinition<Account> updateDef = json;
this is off the top of my head and untested. the only thing i'm unsure of (without an IDE) is the .Document.ToString() part above.

Building json response

I'm working on a self hosting rest api used to monitor de status of several servers.
I was tasked that, when everything is working correctly, I should only return
{"response":"ok"}
But, when there's an error on queried server, or servers, I must return
{ "response" : [ {"agent":"<server>:<port>","port":"<port>" ,"Error":"<Description of the error>"} ] }
I was thinking on building a helper class to build object on this schema and returning them over the rest api
public class HelperErrorResponseClass
{
public string agent { get; set; }
public string port { get; set; }
public string Error { get; set; }
}
This is no problem, the issue is, how to deal when everything it ok. I have this Api response helper class
public class Response
{
public string response { get; set; }
}
But I'm seeing that I'll need to change the response property to List<HelperErrorResponseClass> in order to send the error response. Do you think that, if I stringify the List<HelperErrorResponseClass> object with Json.Net it will be returned in the desired format?
Edit: Forgot to add that, I-m using Web Api to build the rest service.
UDPATE:
After further research, I found a way to work this out.
Following this post, I was able to rewrite the helper classes like this
[DataContract]
[KnownType(typeof(List<HelperErrorResponseClass>))]
public class Response
{
[DataMember]
public object response { get; set; }
}
[DataContract]
public class HelperErrorResponseClass
{
[DataMember(EmitDefaultValue = false)]
public string agent { get; set; }
[DataMember(EmitDefaultValue = false)]
public string port { get; set; }
[DataMember(EmitDefaultValue = false)]
public string error { get; set; }
}
This work to fulfill my and my client needs... except for one little thing. When I get the result from a List, and given that I added the KnownTypes directive, my response is now this
{"response":[{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"},{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"}]}
Any idea how to get rid of that __type property of the response? make that it must be explicit to only return the declared properties of the helper class?
Simplest way to deal with this is to set the return type on the handling function to string, then you can check for errors and do something like;
//pseudo code to give an idea
if (errorsList.Count() > 0)
{
return JsonConvert.SerializeObject(errorsList);
}
else
{
return JsonConvert.SerializeObject(new Response("ok"));
}
Now this being said... Unless the people providing requirements aren't at all flexible you should just redo the design. How about just returning the errors array and the person calling the API can infer that if it's length is 0 then everything is working OK. Seems pretty straight forward, right? You could also just put all the properties on one object and those fields would just come back as null or empty strings. Or you could change you serializer settings to exclude them if they don't have a value.
Keep things simple and use an anonymous type.
if (condition)
{
return JsonConvert.SerializeObject(new { response = new { agent = "x", port = "y", error = "z" }});
}
else
{
return JsonConvert.SerializeObject(new { response = "ok"});
}
More info:
https://msdn.microsoft.com/en-us/library/bb397696.aspx
I personally don't think you need a Response class, especially that it is of object type. IMHO, you've overcomplicated the very simple issue that you have. It is not only the __type, but also other info like HelperErrorResponseClass:#AppCommonLib that isn't supposed to be there.
Another Issue you have is the incorrect name of the HelperErrorResponseClass class. This is not a helper class. It is a standard data-object class.
A helper class is a class filled with static methods. It is usually used to isolate a "useful" algorithm.
This is how I would do it:
I'd get rid of the Response class.
I'd use your original simple HelperErrorResponseClass class, but rename it to something more meaningful like ErrorDetails.
I'd return the response like this:
.
if (errorsList.Count() > 0) {
return JsonConvert.SerializeObject(new { response = errorsList});
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}
However, if you really want to stick to your updated solution, an easy way to get rid of the __type is simply removing it from the final serialized string:
if (errorsList.Count() > 0) {
string r = JsonConvert.SerializeObject(new { response = errorsList});
return r.Replace("__type", "");
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}

Access Values using Instance or Properties

I have a complex class and the code looks like this:
public class Person
{
public Address address;
public Address GetAddress { get; set; } //Property
// Constructor
public Person() { address = new Address(); }
}
public class Address
{
// Properties
public String Street { get; set; }
public String City { get; set; }
// Constructor
public Address() { Street = "Madison"; City = "NY"; }
}
My Action Methods(Trimmed Version) are as follows:
public ActionResult Index()
{
Person person = new Person();
return View(person);
}
[HttpPost]
public ActionResult Index(Person person)
{
ViewBag.City = person.address.City;
return View();
}
From my View/.cshtml file:
I can access values for address through Model's instance or the property.
When I bring up the View for the 1st time:
If I access values through instance #Html.TextBoxFor(m => m.address.Street) -- I get the values set in the class constructor - Madison and NY.
If I access it through property - #Html.TextBoxFor(m => m.GetAddress.Street), I get blank values.
Inside my Controller Action method- public ActionResult Index(Person person):
If I access values using instance - person.address.street/city, I get the old values that were assigned to my model - Madison and NY.
If I access values using property - person.GetAddress.Street, I get the updated values that were entered from View.
Thanks in advance.
If I access it through property - #Html.TextBoxFor(m => m.GetAddress.Street), I get blank values
That's because you're never setting the Person.GetAddress property (an odd name for a property, sounds like it should be a method). Therefore you're getting your blank value.
If I access values using property - person.GetAddress.Street, I get the updated values that were entered from View
It sounds like you have the Html.TextBoxFor(m => m.GetAddress.Street), in which case you're populating that property value. Then when you POST you are pulling the person.GetAddress.Street which includes the inputted data.
The reason that GetAddress is giving you a blank text box is because it's not set. It looks like you might want that to be actually giving you the Person's address, so you could change it like so:
GetAddress
{
get { return address; }
}
Or alternatively, simply change the address field to a property:
public Address address { get; set; }
This gives you the ability to get rid of GetAddress altogether if you so choose.
If you were setting GetAddress with your text box, that would cause it to store the values that you pass in. Since, in your code sample, GetAddress is a standalone property that isn't actually attached to the address field of the Person class, then having a text box tied to it will only change the value of the GetAddress property rather than the actual address property as you expected. You might find that my alternative solution for question 1 would solve this problem for you.
Your property is not auto-linked to your field. GetAddress { get; set; } auto-generates a field, and the property acts as syntactic sugar for accessing those. If you just want to wrap address you could do the following:
public Address GetAddress
{
get
{
return address;
}
set
{
address = value;
}
}
Alternately, you can just get rid of your field, and do (which gives you public getters and setters):
public Address Address { get; set; }

Categories