Need a Way to retrieve data from second table using C# Linq in Entity Framework Core - ef-code-first

I'm unable to return the child data from the second table linked buy a Foreign key with the first table.
I have the following Tables that has been created using Entity Framework Core Code first approach :
public class BoardGame
{
public int id { get; set; }
public double price { get; set; }
public List<BoardGameTitle> BoardGameTitle{get;set;}
}
public class BoardGameTitle
{
public int id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int languageId { get; set; }
public int BoardGameId{get;set;}
public BoardGame BoardGame{get;set;}
}
public class TableFlipDB :DbContext{
public TableFlipDB (DbContextOptions<TableFlipDB> options)
:base(options)
{}
public DbSet<BoardGame> BoardGame{get;set;}
public DbSet<BoardGameTitle> BoardGameTitle{get;set;}
protected override void OnModelCreating(ModelBuilder modelBuilder){
modelBuilder.Entity<BoardGame>().HasMany(bg=>bg.BoardGameTitle).WithOne(bg=>bg.BoardGame);
//modelBuilder.Entity<BoardGameTitle>().HasOne(BoardGameTitle=>BoardGameTitle.BoardGame).WithMany(BoardGameTitle=>BoardGameTitle.BoardGameTitle);
}
}
the second table BoardGameTitle contains a Foreign key to the following table BoardGame through the property BoardGameId,
When ever I retrieve the list of board games I cant get the related data from the second table in the property BoardGameTitle the property returns null
my Controller
public ActionResult<List<BoardGame>> GetAll()
{
return TableFlipDB.BoardGame.ToList();
}
Expected Result was:
[
{
"id": 1,
"price": 100,
"boardGameTitle": [
{
"id": 1,
"name": "b1",
"description": "b1",
"languageId": 1,
"boardGameId": 1,
"boardGame": {
"id": 1,
"price": 100,
"boardGameTitle": []
}
}
]
},
{
"id": 2,
"price": 200,
"boardGameTitle": [
{
"id": 2,
"name": "b2",
"description": "b2",
"languageId": 1,
"boardGameId": 2,
"boardGame": {
"id": 2,
"price": 200,
"boardGameTitle": [
{
"id": 3,
"name": "b22",
"description": "b22",
"languageId": 2,
"boardGameId": 2
}
]
}
},
{
"id": 3,
"name": "b22",
"description": "b22",
"languageId": 2,
"boardGameId": 2,
"boardGame": {
"id": 2,
"price": 200,
"boardGameTitle": [
{
"id": 2,
"name": "b2",
"description": "b2",
"languageId": 1,
"boardGameId": 2
}
]
}
}
]
}
]
But the Result I got
[
{
"id": 1,
"price": 100,
"boardGameTitle": null
},
{
"id": 2,
"price": 200,
"boardGameTitle": null
}
]

I have found the solution as #Elyas Esna in the comments section said I need to change the controller return statement from return TableFlipDB.BoardGame.ToList(); to return TableFlipDB.BoardGame.Include(p=>p.BoardGameTitle).ToList();

Related

Automapper Parent Child Mapping sets Grandchildren Properties as null

Automapper seems to be ignoring grandchild objects when mapping between objects. So that i only get 2 levels of recursion. I need at least 3, ideally n levels.
How do I get Automapper to return the correct object with all children intact?
I can work around it, by serializing my DbPOCO and deserializing to the Dto, but that means crossing concerns in my application, and/or doing something different for this specific method which I don't like.
Given my DB Poco
public class Menu : SqlTable, IParentChild<Menu>
{
public string Label {get; set;}
public string Route {get; set;}
public float Sequence { get; set; }
public int? ParentID {get; set;}
public Menu Parent {get; set;}
public IList<Menu> Children { get; set; }
}
I can return (with some recursive magic) an object which serializes like this, but it includes (removed) database audit columns and other table generic items that I don't want communicated over my API.
[
{
"Label": "Main Menu 1",
"Route": "",
"Sequence": 1,
"ParentID": null,
"Parent": null,
"Children": [
{
"Label": "Menu 1 Sub 1",
"Route": "",
"Sequence": 1,
"ParentID": 1,
"Parent": null,
"Children": [
{
"Label": "Menu 1 Sub 1 Sub 1",
"Route": "",
"Sequence": 1,
"ParentID": 2,
"Parent": null,
"Children": [],
...
},
{
"Label": "Menu 1 Sub 1 Sub 2",
"Route": "",
"Sequence": 2,
"ParentID": 2,
"Parent": null,
"Children": [],
...
}
],
...
},
{
"Label": "Menu 1 Sub 2",
"Route": "",
"Sequence": 2,
"ParentID": 1,
"Parent": null,
"Children": [],
"ID": 5,
...
}
],
...
},
{
"Label": "Main Menu 2",
"Route": "",
"Sequence": 2,
"ParentID": null,
"Parent": null,
"Children": [
{
"Label": "Menu 2 Sub 1",
"Route": "",
"Sequence": 1,
"ParentID": 6,
"Parent": null,
"Children": [],
...
}
],
...
}
]
When i pass this into Automapper, to map it to the following DTO
public class WebApplicationMenuModel
{
public string Label { get; set; }
public string Route { get; set; }
public float Sequence { get; set; }
public IEnumerable<WebApplicationMenuModel> Children { get; set; }
}
The grandchildren from my original object are set to null
[
{
"Label": "Main Menu 1",
"Route": "",
"Sequence": 1,
"Children": [
{
"Label": "Menu 1 Sub 1",
"Route": "",
"Sequence": 1,
"Children": null
},
{
"Label": "Menu 1 Sub 2",
"Route": "",
"Sequence": 2,
"Children": null
}
]
},
{
"Label": "Main Menu 2",
"Route": "",
"Sequence": 2,
"Children": [
{
"Label": "Menu 2 Sub 1",
"Route": "",
"Sequence": 1,
"Children": null
}
]
}
]
The map profile is fairly basic
public class MenuProfile : Profile
{
public MenuProfile()
{
CreateMap<Menu, WebApplicationMenuModel>()
.ReverseMap();
}
}
See Reply by Lucian Bargaoanu. Needed to user the master Automapper branch (9.1.0-ci-01627) to support this from MyGet.
https://docs.automapper.org/en/stable/The-MyGet-build.html

ServiceStack: OpenApi import in Azure Api Management Gateway

We are running a Dotnet Core 2.2 service using ServiceStack 5.7, and need to throttle it. So we want to put it behind a Azure Api Management Gateway (apim) - it runs in a Azure App Service.
We have enabled OpenApi feature using
self.Plugins.Add(new OpenApiFeature());
When we export our OpenApi definition we get the following:
"paths": {
...
"/api/search": {
"post": {
"tags": [
"api"
],
"operationId": "SearchRequestsearch_Post",
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "Filters",
"in": "formData",
"type": "array",
"items": {
"$ref": "#/definitions/FilterDto"
},
"collectionFormat": "multi",
"required": false
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/SearchResponse"
}
}
},
"deprecated": false,
"security": [
{
"Bearer": []
}
]
},
"parameters": [
{
"$ref": "#/parameters/Accept"
}
]
}
}
...
"definitions": {
"FilterDto": {
"title": "FilterDto",
"properties": {
"Field": {
"description": "The field to filter on",
"type": "string",
"enum": [
"None",
"DestinationName",
"DocumentId"
]
},
"Values": {
type": "array",
"items": {
"type": "string"
}
},
"Type": {
"type": "string",
"enum": [
"Equals",
"NotEquals",
"RangeNumeric",
"RangeDate"
]
}
},
"description": "FilterDto",
"type": "object"
}
...
}
The problem is that it is not supported to have a parameter with an array of a type (defined in #/definitions/FilterDto). And it fails with:
Parsing error(s): JSON is valid against no schemas from 'oneOf'. Path 'paths['/api/search'].post.parameters[1]', line 1, position 666.
Parsing error(s): The input OpenAPI file is not valid for the OpenAPI specificate https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md (schema https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v2.0/schema.json).
In the Azure portal.
In c# (ServiceStack) we have defined the following:
public class SearchRequest : SearchRequestBase, IReturn<SearchResponse>
{
public SearchRequest()
{
Filters = new List<FilterDto>();
}
[ApiMember(Name = "Filters"]
public List<FilterDto> Filters { get; set; }
}
public class FilterDto
{
[ApiMember(Name = "Field"]
[ApiAllowableValues("Field", typeof(FilterAndFacetField))]
public FilterAndFacetField Field { get; set; }
[ApiMember(Name = "Values")]
public List<string> Values { get; set; }
[ApiMember(Name = "Type")]
[ApiAllowableValues("Type", typeof(FilterType))]
public FilterType Type { get; set; }
public FilterDto()
{
Values = new List<string>();
}
}
Have anyone successfully managed to import a OpenApi using array of $ref in the parameters from ServiceStack into a Api Management?

JSON.NET - Getting nested values

I have a JSON something like this:
{
"key": "Target",
"value": {
"__type": "Entity:http://schemas.microsoft.com/xrm/2011/Contracts",
"Attributes": [
{
"key": "prioritycode",
"value": {
"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 1
}
},
{
"key": "completeinternalreview",
"value": false
},
{
"key": "stepname",
"value": "10-Lead"
},
{
"key": "createdby",
"value": {
"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
"Id": "ca2ead0c-8786-e511-80f9-3863bb347b18",
"KeyAttributes": [],
"LogicalName": "systemuser",
"Name": null,
"RowVersion": null
}
}
]
}
}
How do the toke for the key/values by searching for the value of the key?
Eg I want to get the key value pair 'completeinternalreview'
Assuming you have a C# class like this to represent that attributes object from your JSON:
public class MyValue
{
[JsonProperty("Attributes")]
public List<KeyValuePair<string, object>> Attributes { get; set; }
}
you can simply deserialize the string:
var result = JsonConvert.DeserializeObject<KeyValuePair<string, MyValue>>(jsonString);
and then find the correct key-value pair with:
var kvp = result.Value.Attributes.Find(a => a.Value == "completeinternalreview");

NEST mapping of Dictionary<string,List<string>>

I have a class with Dictionary<string, List<string>> as one of the members. I'm able to insert the data with automap, but unable to query the documents with the keys and/or values in the dictionary. If I use match query, it returns everything in the index. I tried using Terms, Nested/non-nested query, and QueryString query, but none of them returns any document.
class ESSchema
{
[String(Index = FieldIndexOption.NotAnalyzed, Store = false)]
public string Machine { get; set; }
[String(Index = FieldIndexOption.NotAnalyzed, Store = false)]
public string Filename { get; set; }
[Number(NumberType.Long, Store = false)]
public long BatchNumber { get; set; }
[String(Index = FieldIndexOption.NotAnalyzed, Store = false)]
public string Environment { get; set; } = null;
[Nested]
//[Object(Store = false)]
public Dictionary<string, List<string>> KeysValues { get; set; }
}
Automap converts the dictionary to the following mapping which I'm not sure is the correct representation of what I'm looking for.
"keysValues": {
"type": "nested",
"properties": {
"comparer": {
"properties": {
},
"type": "object"
},
"count": {
"type": "integer"
},
"keys": {
"properties": {
"count": {
"type": "integer"
}
},
"type": "object"
},
"values": {
"properties": {
"count": {
"type": "integer"
}
},
"type": "object"
},
"item": {
"type": "string"
}
}
},
When Automapping, NEST by default will map one level down in the object graph. In the case of your Dictionary<string, List<string>> property, the public properties of the dictionary end up being mapped, which is undesirable.
There are a couple of ways this can be controlled
1.Passing -1 for maxRecursion to AutoMap()
class ESSchema
{
[String(Index = FieldIndexOption.NotAnalyzed, Store = false)]
public string Machine { get; set; }
[String(Index = FieldIndexOption.NotAnalyzed, Store = false)]
public string Filename { get; set; }
[Number(NumberType.Long, Store = false)]
public long BatchNumber { get; set; }
[String(Index = FieldIndexOption.NotAnalyzed, Store = false)]
public string Environment { get; set; } = null;
[Object(Store = false)]
public Dictionary<string, List<string>> KeysValues { get; set; }
}
client.Map<ESSchema>(m => m
.AutoMap(-1)
);
which results in
{
"properties": {
"machine": {
"type": "string",
"store": false,
"index": "not_analyzed"
},
"filename": {
"type": "string",
"store": false,
"index": "not_analyzed"
},
"batchNumber": {
"type": "long",
"store": false
},
"environment": {
"type": "string",
"store": false,
"index": "not_analyzed"
},
"keysValues": {
"type": "object",
"store": false,
"properties": {}
}
}
}
2.Controlling the mapping through fluent mapping overrides
client.Map<ESSchema>(m => m
.AutoMap()
.Properties(p => p
.Object<Dictionary<string, List<string>>>(o => o
.Name(n => n.KeysValues)
.Store(false)
)
)
);
Using .Properties() overrides any inferred mapping from Automapping.

JSON DeserializeObject error for a string and an object (array)

This is my JSON value
{ "spatialReference":
{ "wkid": 4326, "latestWkid": 4326 },
"candidates":
[
{ "address": "380 New York St, Redlands, California, 92373", "location": { "x": -117.19564110200449, "y": 34.057084093752607 }, "score": 99.890000000000001, "attributes": { } },
{ "address": "380 New York St, Redlands, California, 92373", "location": { "x": -117.19564235064003, "y": 34.057118309276547 }, "score": 99.890000000000001, "attributes": { } },
{ "address": "381 New York St, Redlands, California, 92373", "location": { "x": -117.19570747666533, "y": 34.057117640108572 }, "score": 78.890000000000001, "attributes": { } },
{ "address": "New York St, Redlands, California, 92373", "location": { "x": -117.19564313410432, "y": 34.054959327808675 }, "score": 99.890000000000001, "attributes": { } },
{ "address": "92373, Redlands, California", "location": { "x": -117.18448413080158, "y": 34.042938592774277 }, "score": 100, "attributes": { } },
{ "address": "Redlands, California", "location": { "x": -117.18259971082223, "y": 34.055564407815503 }, "score": 98.780000000000001, "attributes": { } }
]
}
This is code:
public class CanAttributes
{
public string StreetName { get; set; }
public string StreetType { get; set; }
}
public class Candidates
{
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("location")]
public GeoLocation Location { get; set; }
[JsonProperty("score")]
public decimal Score { get; set; }
[JsonProperty("attributes")]
public CanAttributes Attributes { get; set; }
}
public class jsCandidates
{
[JsonProperty("spatialReference")]
public string spatialReference { get; set; }
[JsonProperty("candidates")]
public Candidates[] candidates { get; set; }
}
public class GeoLocation
{
[JsonProperty("x")]
public decimal X { get; set; }
[JsonProperty("y")]
public decimal Y { get; set; }
}
jsCandidates canArray = JsonConvert.DeserializeObject<jsCandidates>(str);
It gives me "Error reading string. Unexpected token: StartObject. Path 'spatialReference', line 2, position 23" when I tried to deserialized the above json value.
Any help would be appreciated.
Not string, you should change to object.
public string spatialReference { get; set; }
Or this instead:
public class SpatialReference
{
public int wkid { get; set; }
public int latestWkid { get; set; }
}

Resources