Azure Table TableClient Response - ETag - c#

Posted a few variations of a theme on this question but don't seem to have asked the right question and have also dug more into my problem.
So...
I am querying Azure Table Storage using the Azure.Data.Table TableClient with the GetEntityAsync<Type>(partitionKey, rowKey) method
Update - TableClient Calling Code
using the following code:
Initialize the tableclient:
public AzureTableService(string tableName, string storageUri, string accountName, string storageAccountKey)
{
tableClient = new TableClient(
new Uri(storageUri),
tableName,
new TableSharedKeyCredential(accountName, storageAccountKey));
}
Get the TableEntity
public async Task<TableEntity> GetEntityAsync(string partitionKey, string rowKey)
{
var response = await tableClient.GetEntityAsync<TableEntity>(partitionKey, rowKey);
return response.Value;
}
in two ways:
With TableEntity as the type
With User as the type
TableEntity is the type Azure.Data.Table.TableEntity which itself inherits Azure.Data.Table.ITableEntity. ITableEntity defines the properties ETag Etag, string PartitionKey, string RowKey, and DateTimeoOffset? Timestamp.
User is a class which inherits from ITableEntity and therefore has those same properties alongside strings for Forename, Surname, Mobile and Address.
Update - User Object Definition
public class User : ITableEntity
{
public User() { }
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public DateTimeOffset? Timestamp { get; set; }
public ETag ETag { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public string Mobile { get; set; }
public string Address { get; set; }
}
As such both are valid to use as the type for the method GetEntityAsync<type> as they both implement the same interface.
If I use the User type the returned object serialised as Json is:
{
"partitionKey": "partitionKey",
"rowKey": "rowKey",
"timestamp": "2023-02-09T22:28:54.0242121+00:00",
"eTag": {},
"forename": "forename",
"surname": "surname",
"mobile": "mobile",
"address": "address"
}
If I use the TableEntity type the returned object serialised as Json is:
{
"odata.etag": "W/\"datetime'2023-02-09T22%3A28%3A54.0242121Z'\"",
"PartitionKey": "partitionKey",
"RowKey": "rowKey",
"Timestamp": "2023-02-09T22:28:54.0242121+00:00",
"Address": "address",
"Forename": "forename",
"Mobile": "mobile",
"Surname": "surname"
}
Neither is populating the ETag correctly - User has a value of ETag: {} and TableEntity has a valid string value of an ETag "W/\"datetime'2023-02-09T22%3A28%3A54.0242121Z'\"" BUT it is is a string with a property name of odata.etag NOT an object of type ETag named ETag as per the ITableEntity implementation.
Further to this...
when I do a delete operation and require the ETag ifMatch for validation the validation works using the odata.etag property value and seemingly not using the ETag property as implemented with ITableEntity.
Update - Delete Method
public async Task<bool> DeleteItemAsync(TableEntity item, bool forceDelete)
{
var response = await tableClient.DeleteEntityAsync(item.PartitionKey, item.RowKey, forceDelete? ETag.All : item.ETag);
if (response.Status == 200 || response.Status == 201 || response.Status == 204) { return true; }
else { throw new Exception($"The response was {response.Status} - {response.ReasonPhrase}"); }
}
So in effect - the ITableEntity implements a property ETag that is not being set and does not support the ETag property for validation.
I am SO confused - what do I need to do to get the ITableEntity ETag property assigned using the Azure.Data.Table.GetEntityAsync<TableEntity>(PK, RK) method and used in the Azure.Data.Table.DeleteEntityAsync(PK, RK, ETag) validation?

Related

How to include members of base entity in Swagger API Documentation?

I have something like these:
BaseDTO
public record BaseDTO
{
public virtual int Id { get; protected init; }
public DateTime Timestamp { get; protected init; }
}
NotificationDTO
public record NotificationDTO(string Code, string Name, string Description, NotificationType Type) : BaseDTO;
NotificationsController
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<IActionResult> Post([FromBody] NotificationDTO notificationDTO)
{
await _notificationsService.AddNotification(notificationDTO);
return Ok();
}
When I open SwaggerURL only members of NotificationsDTO can be set in "Try it out" section, members of BaseDTO are missing. Even if I add them by hand like setting the Id to 1 in Swagger, if I put a breakpoint in controller, Id it's 0, so Swagger doesn't modify it.
How can I see the base entity members in Swagger?
Edit:
It seems the object binding is not done properly, I have tried to send this in the request body with Postman:
{
"type": 0,
"id": 1,
"timestamp": "2022-05-13T09:22:18.429Z",
"code": "string",
"name": "string",
"description": "string"
}
And in the controller I've got this:
{
"NotificationDTO"{
Id = 0,
"Timestamp = 1/1/0001 12":"00":00 AM,
"Code = string",
"Name = string",
"Description = string",
"Type = Email"
}
}
If I change the access modifiers of the properties of base entity from public virtual int Id { get; protected init; } to public virtual int Id { get; init; } then the objects are binded properly and I can see the members of the base entity in Swagger.

EF Core's 'HasDefaultValueSql' cannot set current datetime

I have a model for a UserProfile which contains a field for when the user is created. I would like EF Core to automatically set the value of this field to the current datetime, whenever a new row is added to the database. To achieve this, I have made the following model:
public class UserProfile
{
public UserProfile(string username, string email)
{
Username = username ?? throw new ArgumentNullException(nameof(username));
Email = email;
}
public Guid Id { get; init; } = Guid.NewGuid();
public string Username { get; init; }
public string Email { get; private set; } = "";
public DateTime Created { get; init; }
}
In the DbContext I have overridden the OnModelCreating() method and specified that the field UserProfile.Created should by default be set to the SQL value GETDATE() (I have also tried with CURRENT_TIMESTAMP).
public class UserProfileContext : DbContext
{
public UserProfileContext(DbContextOptions<UserProfileContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProfile>().ToTable("Account");
modelBuilder.Entity<UserProfile>()
.Property(u => u.Created)
.HasDefaultValueSql("GETDATE()");
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
But when I create a new user and request it back, I just get the following response:
[
{
"id": "3a33c8ad-7581-4adc-8c91-a65d40ec008e",
"username": "string",
"email": "string",
"created": "0001-01-01T00:00:00"
}
]
I cannot look directly in the database, since I'm using EF Core InMemory. What am I missing here?

C# - Newtonsoft : Generic class for Client Response

After hours of attempts and research, I am asking for your help.
I am calling a public API which returns the same structure except for the datas returned.
For examples, the REST calls which retrieve stations and districts return those two JSON answers :
Stations response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : {
"station": [{
"number": "stationId",
"name": "stationName",
"address": "stationAddress",
"state": "1",
"latitude": "stationLat",
"longitude": "stationLong",
"slotsavailable": "10",
"bikesavailable": "20",
"pos": "0",
"district": "stationDistrict",
"lastupdate": "2016-03-28T11:47:08+02:00"
}, {...}, ...]}
}
Districts response :
"response" : {
"status": { "#attributes": {"code": "0", "message": "OK"} },
"data" : { "district": [{"id": "districtId", "name": "districtName"}, {...}, ...] }
}
I am using a .NET 4.5/C# solution with Newtonsoft.Json to execute the call.
I want to make the object, mapped to the client response, generic so the execution of the call will be made as follow :
var result = await client.Execute<Response<ApiResponseDistrict>>(request);
var result = await client.Execute<Response<ApiResponseStation>>(request);
My first attempt was to make a non generic call (create a full object by returned datas) which was a success.
My second attempt was to created a generic object so I made the following classes using the JsonProperty of the library Newtonsoft :
public class ApiResponse<T>
{
[JsonProperty("response")]
public Response<T> Response { get; set; }
}
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
public class ApiResponseDistrict
{
[JsonProperty("district")]
public List<District> Districts { get; set; }
}
public class District
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
At this point, when I am executing the call the object Response is valorized and also its property Status with the value waited but the property Data is never valorized (null).
My third attempt was to continue on the second attempt but using the JsonObject of the Newtonsoft library which it's given (with the same result) :
[JsonObject("district")]
public class ApiResponseDistrict
{
public List<District> Districts { get; set; }
}
As I am new to Newtonsoft, I would like to know if it is possible to use generic classes, as I am trying to do, to mapped the object returned by the call or I have to create a complete object for each "data" returned ?
Thank you for your answer and explanations or clues for me to find the answer !
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public Data<T> Data { get; set; }
}
public class Data<T>
{
public T ResponseData { get; set; }
}
This adds another layer between the data, so a response would look like this:
{
"Status": …,
"Data": {
"ResponseData": {
<The actual type T>
}
}
}
Instead, you want to remove that ResponseData level:
public class Response<T>
{
[JsonProperty("status")]
public Status Status { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
So for example, for the JSON above, you would have a StationResponseData class:
public class StationResponseData
{
public List<Station> Stations
{ get; set; }
}
And then you would deserialize the JSON as Response<StationResponseData>. The Station class would then contain those properties for number, name, address, etc.

RestSharp - deserialize json response with invalid key name (contains a period )

I've been stuck on this for awhile. I have a JSON response sending me keys that include periods. For example: "cost_center.code"
How can I get this into my object? I'm not getting any errors but the value is just coming in as null and isn't being deserialized into my class.
Here's my classes:
public class Result
{
public string company { get; set; }
public string first_name { get; set; }
public string email { get; set; }
public string employee_id { get; set; }
public string last_name { get; set; }
[DeserializeAs(Name="cost_center.code")]
public string cost_center { get; set; }
}
public class RootObject
{
public List<Result> result { get; set; }
}
Here's the JSON response:
{
"result": [
{
"company": "My Company",
"first_name": "First",
"email": "example#fakeaddress.com",
"employee_id": "123456789",
"last_name": "Last",
"cost_center.code": "12345"
}
]
}
I execute with:
var response = client.Execute<List<RootObject>>(request);
// this returns null
Console.WriteLine(response.Data[0].result[0].cost_center);
// all other values return fine ex:
Console.WriteLine(response.Data[0].result[0].company);
I've tried both with and without the DeserializeAs. I'm not sure its even working. Am I using this property incorrectly? Is it a container issue with the List?
Edited and accepted the answer below to use JsonProperty. For others who may come along this was the solution.
Added JSON.net nuget.
using Newtonsoft.Json;
Set the JsonProperty as described:
[JsonProperty("cost_center.code")]
Changed my execute to:
var response = client.Execute(request);
Then deserialized it like this:
var jsonResponse = JsonConvert.DeserializeObject<RootObject>(response.Content);
Afterwards I can access the value:
Console.WriteLine(jsonResponse.result[0].CostCenter
Do the following with properties having period in their names :
[JsonProperty("cost_center.code")]
public string CostCenter{ get; set; }
It should work
If you want to user RestSharp natively or couldn't get the Newtonsoft.Json.JsonSerializer support to work (which I couldn't), they just added support for proper deserialization of properties with dots in their names as of 106.1.0.
See my response here: Accessing properties with a dot in their name

MVC 4 Web API How to prepare JSON body for Request

Hi I have a function that will expose to the RestAPI. I need to pass in a JSON string to be a defined class. I use the following class for JSON format:
public class Registrant : Guest
{
public int RegistrantID { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public int EventID { get; set; }
public bool IsPrimary { get; set; }
}
And I have the following function in my controller:
public HttpResponseMessage Create(string id, [FromBody]Registrant registrant)
However When I pass in the JSON string the same structure, it could not deserialize correctly.
Is there anything wrong with it?
PS: my JSON string in request body:
{
"RegistrantID":"0",
"Email": "abc#abc.com",
"Phone": "123456789",
"EventID": "8",
"IsPrimary": "true",
"CustomerId": "12345678",
"FirstName": "abc",
"LastName": "def"
}
Update:
The problem is resolved by choosing Content-Type as Application/Json
Also, I took out the quotes on int and bool params it works fine.
Finally the api call look like this in project:
public HttpResponseMessage Create([FromUri]string id, [FromBody]Registrant registrant)

Categories

Resources