Mobile Azure Service C# .NET Backend PATCH not updating - c#

I've created a simple .NET backend mobile azure service in C#. I have the mobile service up and running (all it's doing currently is working with your normal CRUD on a single table). The problem I'm having is that the PATCH/UPDATE will not do as it says. I can do everything else I've tried, SELECT, INSERT, DELETE, but I've been unable to update data.
When I debug into the block of code that calls UpdateAsync, the patch.GetEntity()..... always has the values NULL or zeroed out or day one datetimes, like it's not passing along the property values of what I'm trying to update. The only value I ever have is the correct id. Below I tried to strip out some of the code I have, I used some of what was in the first few tutorials on the Azure website.
I have a Data Object:
public class AdminLookupDATA: EntityData
{
public string description { get; set; }
public int lookuptype { get; set; }
public int priority { get; set; }
public bool inactive { get; set; }
public DateTime createdate { get; set; }
public DateTime editdate { get; set; }
}
I have a DTO:
public class AdminLookupDTO
{
public string id { get; set; }
public string description { get; set; }
public int lookuptype { get; set; }
public int priority { get; set; }
public bool inactive { get; set; }
public DateTime createdate { get; set; }
public DateTime editdate { get; set; }
}
I have a Model:
public class AdminLookupModel
{
public string Id { get; set; }
public string description { get; set; }
public int lookuptype { get; set; }
public int priority { get; set; }
public bool inactive { get; set; }
public DateTime createdate { get; set; }
public DateTime editdate { get; set; }
}
My PATCH inside my controller:
public Task<AdminLookupDATA> PatchAdminLookupDATA(string id, Delta<AdminLookupDATA> patch)
{
return UpdateAsync(id, patch);
}
Also, I have the same issue if I try to run the PATCH function directly from the browser in the "try it out" section, so it's something I have configured wrong within the service project itself. Any suggestions would be greatly appreciated.
Thanks,
Rob

Your property names must start with a capital letter.
Otherwise configure the CamelCasePropertyNamesContractResolver as indicated here:
http://odetocode.com/blogs/scott/archive/2013/03/25/asp-net-webapi-tip-3-camelcasing-json.aspx
code snippet
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();

Related

Web API hangs suddenly

I have a web API in my Blazor project. I have a method that looks like this:
[HttpGet("selected/{userId}/{max}", Name = "GetItems")]
public IList<ItemDto> GetItems(int userId, int max)
{
IList<ItemDto> Items = _itemsDatabaseController.GetItems(userId, max);
return Items?.Count > 0 ? Items : null;
}
Then I generated client code using NSwag Studio. It all worked fine until some days ago when suddenly my API method hangs after leaving the method (after I step over return and } in debugger just nothing happens).
I read somewhere it might be a problem with JSON serialization if you have nested objects in your DTOs, which I have. But its "just" that in ItemDto I have two other objects, I dont understand why it cant serialize that (if that is the problem at all).
Any ideas why it hangs?
These are my DTO's
public class ItemDto
{
public int ItemId { get; set; }
public int CompanyId { get; set; }
public int CustomerId { get; set; }
public int UserId { get; set; }
public CustomerDto CustomerData { get; set; }
}
public class CustomerDto
{
public int CustomerId { get; set; }
public int CustomerSettingsId { get; set; }
public ObjectDto ObjectData { get; set; }
}
public class ObjectDto
{
public int ObjectId { get; set; }
public string Reference { get; set; }
public DateTime? TimeCreated { get; set; }
public DateTime? TimeUpdated { get; set; }
}
I am running on .NET Core 3.0. I have not changed the JSON serializer and it should be the default one that is used by Blazor.

JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than

In my web API when I run project to get data from the database got this error
.net core 3.1
JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.
These are my codes:
my Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
my productCategory class is:
public class ProductCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string CatText { get; set; }
public string ImagePath { get; set; }
public int Priority { get; set; }
public int Viewd { get; set; }
public string Description { get; set; }
public bool Active { get; set; }
public DateTime CreateDate { get; set; }
public DateTime ModifyDate { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
my repo is
public async Task<IList<Product>> GetAllProductAsync()
{
return await _context.Products.Include(p => p.ProductCategory).ToListAsync();
}
my interface
public interface IProductRepository
{
...
Task<IList<Product>> GetAllProductAsync();
...
}
and this is my controller in api project
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public ActionResult Get()
{
return Ok(_productRepository.GetAllProduct());
}
}
When I run API project and put this URL: https://localhost:44397/api/products
I got that error,
I can't resolve it
this is happening because your data have a reference loop.
e.g
// this example creates a reference loop
var p = new Product()
{
ProductCategory = new ProductCategory()
{ products = new List<Product>() }
};
p.ProductCategory.products.Add(p); // <- this create the loop
var x = JsonSerializer.Serialize(p); // A possible object cycle was detected ...
You can not handle the reference loop situation in the new System.Text.Json yet (netcore 3.1.1) unless you completely ignore a reference and its not a good idea always. (using [JsonIgnore] attribute)
but you have two options to fix this.
you can use Newtonsoft.Json in your project instead of System.Text.Json (i linked an article for you)
Download the System.Text.Json preview package version 5.0.0-alpha.1.20071.1 from dotnet5 gallery (through Visual Studio's NuGet client):
option 1 usage:
services.AddMvc()
.AddNewtonsoftJson(
options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
// if you not using .AddMvc use these methods instead
//services.AddControllers().AddNewtonsoftJson(...);
//services.AddControllersWithViews().AddNewtonsoftJson(...);
//services.AddRazorPages().AddNewtonsoftJson(...);
option 2 usage:
// for manual serializer
var options = new JsonSerializerOptions
{
ReferenceHandling = ReferenceHandling.Preserve
};
string json = JsonSerializer.Serialize(objectWithLoops, options);
// -----------------------------------------
// for asp.net core 3.1 (globaly)
services.AddMvc()
.AddJsonOptions(o => {
o.JsonSerializerOptions
.ReferenceHandling = ReferenceHandling.Preserve
});
these serializers have ReferenceLoopHandling feature.
Edit : ReferenceHandling changed to ReferenceHandler in DotNet 5
but if you decide to just ignore one reference use [JsonIgnore] on one of these properties. but it causes null result on your API response for that field even when you don't have a reference loop.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
// [JsonIgnore] HERE or
public virtual ProductCategory ProductCategory { get; set; }
}
public class ProductCategory
{
public int Id { get; set; }
// [JsonIgnore] or HERE
public ICollection<Product> products {get;set;}
}
.NET 5 Web API
public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
}
I have the same issue, my fix was to add async and await keyword since I am calling an async method on my business logic.
Here is my original code:
[HttpGet]
public IActionResult Get()
{
//This is async method and I am not using await and async feature .NET which triggers the error
var results = _repository.GetAllDataAsync();
return Ok(results);
}
To this one:
HttpGet]
public async Task<IActionResult> Get()
{
var results = await _repository.GetAllDataAsync();
return Ok(results);
}
In .Net 6, you can use System.Text.Json to initialize a startup action with AddControllersWithViews like this in Program.cs,
using System.Text.Json.Serialization;
builder.Services.AddControllersWithViews()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
also you can use AddMvc like this,
builder.Services.AddMvc()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
but quote from Ryan
asp.net core 3.0+ template use these new
methodsAddControllersWithViews,AddRazorPages,AddControllers instead of
AddMvc.
I will recommend to use the first solution.
Ensure you have [JsonIgnore] on the correct fields to avoid a circular reference.
In this case you will need
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
You probably don't need the ProductCategoryId field (depends if you are using EF and code first to define your DB)
Edit - In answer to noruk
There is often confusion in connected objects and navigation properties. You can get the data you want in JSON but also define the EF structures to get the correct DB structure (foreign keys, indexes, etc).
Take this simple example. A Product (for example a T-Shirt) has many sizes or SKUs (e.g. Small, Large, etc)
public class Product
{
[Key]
[MaxLength(50)]
public string Style { get; set; }
[MaxLength(255)]
public string Description { get; set; }
public List<Sku> Skus { get; set; }
}
public class Sku
{
[Key]
[MaxLength(50)]
public string Sku { get; set; }
[MaxLength(50)]
public string Barcode { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
// One to Many for Product
[JsonIgnore]
public Product Product { get; set; }
}
Here you can serialise a Product and the JSON data will include the SKUs. This is the normal way of doing things.
However if you serialise a SKU you will NOT get it's parent product. Including the navigation property will send you into the dreaded loop and throw the "object cycle was detected" error.
I know this is limiting in some use cases but I would suggest you follow this pattern and if you want the parent object available you fetch it separately based on the child.
var parent = dbContext.SKUs.Include(p => p.Product).First(s => s.Sku == "MY SKU").Product
I fixed my API Core Net6.0 adding [JsonIgnore]:
public class SubCategoryDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public int CategoryId { get; set; }
[JsonIgnore]
public Category Category { get; set; }
}
For net core 3.1 you have to add in Startup.cs:
services.AddMvc.AddJsonOptions(o => {
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
o.JsonSerializerOptions.MaxDepth = 0;
})
and import at least this package using nuget.org include prerelease:
<PackageReference Include="System.Text.Json" Version="5.0.0-rc.1.20451.14" />
following code is working for me in dotnet 5.0 :
services.AddControllersWithViews()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
Finally fixed mine with System.Text.Json not NewtonSoft.Json using
var options = new JsonSerializerOptions()
{
MaxDepth = 0,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true
};
Using options to serialize
objstr = JsonSerializer.Serialize(obj,options);
My project built with a similar error.
Here's the code before
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
public PrimaryClass primaryClass { get; set; }
}
I took away the PrimaryClass object from the DependentClass model.
Code after
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
}
I also had to adjust the OnModelCreating method from
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses).WithOne(d => d.primaryClass).HasForeignKey(d => d.PrimaryClassId);
to
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses);
The DbSet query that's running is
public async Task<List<DependentClass>> GetPrimaryClassDependentClasses(PrimaryClass p)
{
return await _dbContext.DependentClass.Where(dep => dep.PrimaryClassId == p.PrimaryClassId).ToListAsync();
}
The error could have been with any of these 3 sections of code, but removing the primary object reference from the dependent class and adjusting the OnModelCreating resolved the error, I'm just not sure why that would cause a cycle.
In my case the problem was when creating the entity relationships. I linked the main entity using a foreign key inside the dependent entity like this
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
also I referred the dipendend entity inside the main entity as well.
public List<Device> devices { get; set; }
which created a cycle.
Dependent Entity
public class Device
{
[Key]
public int id { get; set; }
public int asset_number { get; set; }
public string brand { get; set; }
public string model_name { get; set; }
public string model_no { get; set; }
public string serial_no { get; set; }
public string os { get; set; }
public string os_version { get; set; }
public string note { get; set; }
public bool shared { get; set; }
public int week_limit { get; set; }
public bool auto_acceptance { get; set; }
public bool booking_availability { get; set; }
public bool hide_device { get; set; }
public bool last_booked_id { get; set; }
//getting the relationships category 1 to many
public int category_id { get; set; }
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
public List<Booking> Bookings { get; set; }
}
Main Entity
public class Device_Category
{
public int id { get; set; }
public string name { get; set; }
public List<Device> devices { get; set; }
}
}
So I commented the
public List<Device> devices { get; set; }
inside main entity (Device_Category) and problem solved

nested json deserialization - need another set of eyes

I have been trying to get this json to deserialize for two days now using RestSharp. I have gone through the RestSharp github site, looked at countless examples, and spent much time here on Stack Overflow to try and find the answer to no avail. My code had previously worked perfectly but the vendor changed their API version and I was forced to do an update to keep using the application for my legal practice. My json is as follows(client info has been removed and replaced with generic info):
{
"data":[
{
"id":1035117666,
"client":
{
"id":905422394,
"name":"client1"
},
"display_number":"11-00012",
"description":"General",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2011-12-14",
"close_date":null,
"billing_method":"hourly"
},
{
"id":1035117768,
"client":
{
"id":905422506,
"name":"client2"
},
"display_number":"12-00037",
"description":"HOA",
"practice_area":
{
"id":4269978,
"name":"Business"
},
"status":"Open",
"open_date":"2012-08-07",
"close_date":null,
"billing_method":"hourly"
}
],
"meta":
{
"paging":
{
"next":"https://app.goclio.com/api/v4/matters.json?fields=id%2C+client%7Bid%2C+name%7D%2C+display_number%2C+description%2C+practice_area%7Bid%2C+name%7D%2C+status%2C+open_date%2C+close_date%2C+billing_method&limit=2&page_token=BAh7BjoLb2Zmc2V0aQc%3D--b1ea3eba20c8acefbcdfc7868debd1e0ee630c64&status=Open"
},
"records":91
}
}
I built the following schema within my c# code:
public class MatterList
{
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
public class Meta
{
public Paging paging { get; set; }
public int records { get; set; }
}
public class Paging
{
public string previous { get; set; }
public string next { get; set; }
}
[DeserializeAs(Name = "data")]
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
public class PracticeArea
{
public int id { get; set; }
public string name { get; set; }
}
public class Client
{
public int id { get; set; }
public string name { get; set; }
}
When I run the RestSharp deserialize method I am sending the result to an object of type MatterList using the following line of code
MatterList matterList = jsonHandler.Deserialize<MatterList>(response);
I have so far attempted to deserialize without the Meta or Paging POCO classes with the accompanying change to the MatterList class (taking out the Meta property).
I have tried with and without the [DeserializeAs(Name="data")] directive.
I have tried to set the RootElement of the json response prior to deserialization.
I have tried to shorthand the deserialization by combining it with the Execute request code
IRestResponse<MatterList> matterList = client.Execute<MatterList>(request);
I have created a container class called MatterContainer which I placed between MatterList and Matter classes in the schema:
public class MatterList
{
public List<MatterContainer> matters { get; set; }
}
public class MatterContainer
{
public Matter matter { get; set; }
}
public class Matter
{
public int id { get; set; }
public Client client { get; set; }
public string display_number { get; set; }
public string description { get; set; }
public PracticeArea practice_area { get; set; }
public string status { get; set; }
public DateTime open_date { get; set; }
public DateTime close_date { get; set; }
public string billing_method { get; set; }
public string type = "matter";
}
I know I am getting the json response back from the server correctly so my request is proper and MatterList is not null after deserialization. The problem is that I cannot get the deserialization to actually populate the List matters within the MatterList class.
I have been looking at this off and on for two days and cannot get past this hurdle. If anyone sees what I did wrong I would greatly appreciate the insight, I am at a point where I cannot progress further with my application.
Thanks!
I think your [DeserializeAs(Name = "data")] attribute is in the wrong place. Try putting it in the root class instead:
public class MatterList
{
[DeserializeAs(Name = "data")]
public List<Matter> matters { get; set; }
public Meta meta { get; set; }
}
alternatively, try renameing that property to data

Entity Framework - Define Formular Template

I'm about to create an ASP.Net Web Application with MVC and EF for the following scenario:
Every month there are new people entering a department in our company. Until now we're using an Excel Spreadsheet to handle common "Workitems" such as granting filesystem permissions etc.
Now I want to handle those requests using a Webapplication but I'm stuck at creating the Model.
There are two different Requesttypes, "JoinRequest" and "ChangeRequest" for people joining the company and employees changing the department. For each request type there are different workitems defined. Those workitems should be displayed in the webapp when the corresponding request is selected. After the data is loaded the user has to enter the data for the workitems.
E.g.
[JoinRequest]
Add new Filesystem Access permission:
[ UNC Path ] [x] Read [ ] Read & Write
Here the user has to enter the UNC Path and the type of Accesslevel (R or RW).
For now I've got the following Model:
public abstract class DbItem {
[Key]
public int Id { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? DeletionDate { get; set; }
public string CreatedBy { get; set; }
public string DeletedBy { get; set; }
public bool IsDeleted => DeletionDate != null;
}
public abstract class Request : DbItem {
public Department NewDepartment { get; set; }
public DateTime BeginDate { get; set; }
public DateTime? EndDate { get; set; }
public ContractType ContractType { get; set; }
public EmployeeType EmployeeType { get; set; }
public string Location { get; set; }
public string ContactUserId { get; set; }
[NotMapped]
public User Contact {
get {
if (!string.IsNullOrWhiteSpace(ContactUserId)) {
var userSearcher = new ActiveDirectoryUserSearcher();
return userSearcher.FindByUserID(ContactUserId);
}
return null;
}
}
public string Phonenumber { get; set; }
//public virtual List<RequestWorkItem> RequestWorkItems { get; set; }
public bool IsFinished { get; set; }
public abstract string GetRequestType();
}
public abstract class WorkItem : DbItem {
[Key]
[ForeignKey("Language")]
public int LanguageId { get; set; }
[NotMapped]
public virtual Language Language { get; set; }
public string WorkItemText { get; set; }
public virtual Category Category { get; set; }
}
public class JoinRequest : Request {
public Person Person { get; set; }
public override string GetRequestType() {
return "New";
}
}
public class FileSystemAccessWorkItem : AccessWorkItem {
public FileSystemAccessRight FileSystemAccessRight { get; set; }
public string FileSystemPath { get; set; }
}
So when a user visits the website a new request has to be created but for every request type a set of workitems has to be loaded from the database. Additionally for every workitem the data the user entered should be saved somehow.
I hope you understand what I'm looking for - If anything is unclear I'll do my best to explain it more precisely
Edit1:
When looking at my Models I think the only think I achived is defining my Requests (so e.g. a JoinRequest contains some specific WorkItems) but how can I achive that a user is able to use this "RequestTemplate" to create a new Request and fill in the data to the corresponding workitems. Do I need additional Models for that? Maybe an example?

Internal 500 Error with DateTime set in Model

Hey I'm getting a weird internal 500 error. I narrowed it down to finding that the DateTime structure gives me this, but I'm having a hard time understanding why. So I am using asp.net web api and currently storing info as so.
public void StoreFacebookInfo(string accessToken, IdentityUser user)
{
//Retrieve Facebook Mutual Friends
dynamic myMutualFriends = fb.Get(curFbId + "?fields=context.fields(mutual_friends)");
if (myMutualFriends.context.mutual_friends.data != null)
{
foreach (var item in myMutualFriends.context.mutual_friends.data)
{
var f = JsonConvert.DeserializeObject<mutualFriends>(item.ToString());
profileInfo.mutualFriendsList.Add(f);
}
}
//Save all information retrieved from FB into a db
profileInfo.externalId = myInfo.id;
profileInfo.name = myInfo.name;
profileInfo.firstName = firstname.first_name;
profileInfo.lastName = lastname.last_name;
profileInfo.link = link.link;
profileInfo.gender = gender.gender;
profileInfo.imageURL = "https://graph.facebook.com/" + myInfo.id + "/picture?type=large";
profileInfo.locale = locale.locale;
profileInfo.UserId = user.Id;
db.UserInfoes.Add(profileInfo);
db.SaveChanges();
}
}
As you can see, everything looks fine. In my model, I have the following:
public class UserInfo
{
public UserInfo()
{
this.friendsList = new List<friends>();
this.mutualFriendsList = new List<mutualFriends>();
}
[Key]
public int id { get; set; }
public string externalId { get; set; }
public string name { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string link { get; set; }
public string gender { get; set; }
public string imageURL { get; set; }
public string locale { get; set; }
public string language { get; set; }
public string travellevel { get; set; }
public DateTime birthdate { get; set; }
[ForeignKey("UserId")]
public IdentityUser User { get; set; }
public string UserId { get; set; }
public List<mutualFriends> mutualFriendsList { get; set; }
public List<friends> friendsList { get; set; }
}
When I am using DateTime for the birthdate, when it gets to db.saveChanges(), the code runs into an internal 500 error. When I change this DateTime into string the code runs fine and I am able to save the information. This is strange to me because when I run it on another computer, it works completely fine with the DateTime and not changing it to string.
It'd be great if you could explain this error a bit. Thanks for any help.
I think that your problem is the language culture of your client who is different of your server and which badly parse the DateTime (ie: dd/MM/yyyy vs MM/dd/yyyy).
Try to serialize and parse the DateTime in the same format.

Categories

Resources