Getting related data in .Net Core 3.1 - sql-server

I am trying to get related data from 2 tables. I am using .NET Core 3.1, EF Core, and the code-first approach. What I want is to pass a customerid (which is a foreign key in the order table) to get all the orders of that customer. I get some related output but I don't know how to get specific output.
I have commented out the .ThenInclude because it causes an error:
Lambda expression is not valid
Customer model class:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace CRUDRelationship.Models
{
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
//public List<Order> Order { get; set; }
}
}
Order model class:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace CRUDRelationship.Models
{
public class Order
{
[Key]
public int OrderId { get; set; }
public int Total { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
public Customer customer { get; set; }
}
}
Interface:
using CRUDRelationship.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace CRUDRelationship.Data
{
public interface ICustomerData
{
List<Customer> GetCustomers();
Customer GetCustomer(int id);
Customer AddCustomer(Customer customer);
List<Order> GetOrderByCustomer(int CustomerId);
Order AddOrder(Order order);
}
}
Implementation:
using CRUDRelationship.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CRUDRelationship.Data
{
public class CustomerData : ICustomerData
{
private InvetoryContext _invetoryContext;
public CustomerData(InvetoryContext invetoryContext)
{
_invetoryContext = invetoryContext;
}
public Customer AddCustomer(Customer customer)
{
_invetoryContext.Customers.Add(customer);
_invetoryContext.SaveChanges();
return customer;
}
public Order AddOrder(Order order)
{
_invetoryContext.Orders.Add(order);
_invetoryContext.SaveChanges();
return order;
}
public Customer GetCustomer(int id)
{
throw new System.NotImplementedException();
}
public List<Customer> GetCustomers()
{
return _invetoryContext.Customers.ToList();
}
public List<Order> GetOrderByCustomer(int CustomerId)
{
return _invetoryContext.Orders
.Include(order => order.customer)
//.ThenInclude(customer => customer.Name)
.Where(order => order.CustomerId == CustomerId).ToList();
}
}
}
Customer table:
Order table:
Output I get when I pass customer id 2:
[
{
"orderId": 2,
"total": 200,
"customerId": 2,
"customer": {
"customerId": 2,
"name": "Ankit",
"email": "ankit#gmail.com"
}
},
{
"orderId": 4,
"total": 15000,
"customerId": 2,
"customer": {
"customerId": 2,
"name": "Ankit",
"email": "ankit#gmail.com"
}
}
]
This is the output I want:
{
"customer": {
"customerId": 2,
"name": "Ankit",
"email": "ankit#gmail.com",
"Order": [
{
"orderId": 2,
"total": 200,
"customerId": 2
},
{
"orderId": 4,
"total": 15000,
"customerId": 2
}
]
}
}

Related

EF Core self joined ICollection query

I have a problem with querying data from a self reference datatable having an ICollection.
Here are my table structures:
public class SoqHeading
{
public int Id { get; set; }
public string ItemNo { get; set; }
public string Heading { get; set; }
public SoqRevision SoqRevision_NP { get; set; }
public ICollection<SoqItem> Items { get; set; }
public int? ParentSoqHeadingId { get; set; }
public SoqHeading Parent_NP { get; set; }
public ICollection<SoqHeading> Children_NP { get; set; }
}
public class SoqItem
{
public int Id { get; set; }
public SoqPreliminaryOrContract_enum PreliminaryOrContract { get; set; }
public int SoqHeadingId { get; set; }
public SoqHeading SoqHeading_NP { get; set; }
}
I have a self referenced relation in the SoqHeading table which is 1 to many. The SoqHeading table can have zero or many SoqItems.
This is how the data is in my database
what i want is to query all from the root node of the SoqHeading table with its children and the items.
like this
{
"ItemNo": "C",
"Items": [
{
"ItemNo": "c-1",
},
{
"ItemNo": "c-2",
}
],
"Children_NP": [
{
"ItemNo": "C.1",
"Children_NP": [
{
"ItemNo": "C.1.1",
"Items": [
{
"ItemNo": "c.1.1-1",
},
{
"ItemNo": "c.1.1-2",
}
],
"Children_NP": [],
},
{
"ItemNo": "C.1.2",
"Items": [
{
"ItemNo": "c.1.2-1",
},
{
"ItemNo": "c.1.2-2",
}
],
"Children_NP": [],
}
],
},
{
"ItemNo": "C.2",
"Children_NP": [],
}
]
}
But currently i get data from the query like this
[
{
"ItemNo": "C"
},
{
"ItemNo": "C.1.2",
}
]
where C.1.2 has to be inside C, but it not. C and C.1.2 are in the same level which i dont wont to have produced by the following query
var entity = await _context.SoqHeadings
.Include(i=>i.Items)
.Include(i => i.Children_NP)
.Where(w => w.SoqRevision_NP.ProjectId == 10)
.ToListAsync();
please help
so it seems to be EFcore has no support to included the children on the first child node https://github.com/dotnet/efcore/issues/14144
so the work around is something like this here https://khalidabuhakmeh.com/recursive-data-with-entity-framework-core-and-sql-server

Extending types with ObjectType field

I'm new to HotChocolate and GraphQL, and I have some difficulties with type extension.
Is it possible to extend type with ObjectType field? I've found only one example in the documentation with StringType:
protected override void Configure(IObjectTypeDescriptor descriptor)
{
descriptor.Name("Person");
descriptor.Field("address")
.Type<StringType>()
.Resolver("Address");
}
I've tried to do something similar, but I have this exception HotChocolate.SchemaException: Unable to resolve type reference Output: ObjectType.
protected override void Configure(IObjectTypeDescriptor descriptor)
{
descriptor.Name("Person");
descriptor.Field("address")
.Type<ObjectType>()
.Resolver(ctx => new ObjectType(d => d.Field("street").Type<StringType>().Resolver("Street")));
}
Could you please advice any methods to extend type with ObjectType field in my case? Or just answer whether it is possible or not?
Thanks!
Suppose, your class Book contains some JSON field 'Data'. The structure of 'Data' can be changed between restarts but is known prior to any start (i.e. you know both property names and their types at the startup).
The following code addresses that case:
using System.Linq;
using HotChocolate.Resolvers;
using HotChocolate.Types;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
namespace Ademchenko.GraphQLWorkshop
{
public class Book
{
public int Id { get; set; }
public JObject Data { get; set; }
}
public interface IBookService { IQueryable<Book> GetAll(); }
public class InMemoryBookService : IBookService
{
private readonly Book[] _staticBooks = {
new Book {Id = 11, Data = JObject.FromObject(new {Title = "FooBook", AuthorId = 1, Price = 10.2m})},
new Book {Id = 22, Data = JObject.FromObject(new { Title = "BarBook", AuthorId = 2, Price = 20.2m})}
};
public IQueryable<Book> GetAll() => _staticBooks.AsQueryable();
}
public class Query
{
public IQueryable<Book> GetBooks(IResolverContext ctx) => ctx.Service<IBookService>().GetAll();
}
public class BookType : ObjectType<Book>
{
protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
{
descriptor.Field(d => d.Data).Type<DataType>();
}
}
public class DataType : ObjectType
{
protected override void Configure(IObjectTypeDescriptor descriptor)
{
descriptor.Field("title").Type<StringType>().Resolve((ctx, ct) => (string)ctx.Parent<JObject>()["Title"]);
descriptor.Field("authorId").Type<IntType>().Resolve((ctx, ct) => (int)ctx.Parent<JObject>()["AuthorId"]);
descriptor.Field("price").Type<DecimalType>().Resolve((ctx, ct) => (decimal)ctx.Parent<JObject>()["AuthorId"]);
}
}
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration) => Configuration = configuration;
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IBookService, InMemoryBookService>();
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddType<BookType>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) => app.UseRouting().UseEndpoints(endpoints => endpoints.MapGraphQL());
}
}
Making the request to the server:
{
books
{
id,
data
{
title
authorId
price
}
}
}
you will get the following response:
{
"data": {
"books": [
{
"id": 11,
"data": {
"title": "FooBook",
"authorId": 1,
"price": 1
}
},
{
"id": 22,
"data": {
"title": "BarBook",
"authorId": 2,
"price": 2
}
}
]
}
}

EF return columns data as list or array

I have two tables, which have following foreign key
Poll(CreatedBy) --> CreateBy(ID)
On performing join, I am getting data as following:
I want to write an query using EF (if possible) to return result as:
[{
"id": 1,
"users": [
{
"email": "abc.com"
}
]
},
{
"id": 3,
"users": [
{
"email": "xyz.test"
},
{
"email": "tet.com"
}
]
}]
Assuming that the class structure like below:
public class Poll
{
public int ID { get; set; }
public string Name { get; set; }
public List<CreateBy> CreateBy { get; set; }
}
public class CreateBy
{
public int ID { get; set; }
public string EmailAddress { get; set; }
public string DisplayName { get; set; }
[ForeignKey("Poll")]
public int PollId { get; set; }
}
Data source:
Linq query:
var result = (from p in _context.Poll.ToList()
join c in _context.CreateBy on p.ID equals c.PollId
group p by p.ID into g
select new
{
id = g.Key,
users = g.Select(x => x.CreateBy.Where(z => z.PollId == g.Key).Select(y => new { email = y.EmailAddress })).FirstOrDefault()
}).ToList();
Result:

Post an array of complex objects with JSON, MVC model is do not bind

here is my codes.
json_model
var mix = {
MixName: $("#mixname").val(),
MixDesc: tinyMCE.activeEditor.getContent(),
Price: $("#price").val(),
DiseaseMixs: [],
MixProducts: []
}
Add items to DiseaseMixs and MixProducts
$("#DiseaseList").find("tbody tr").each(function (index) {
mix.DiseaseMixs.push({
MixID: parseInt(MixID),
DiseaseID: parseInt($(".diseaseid").eq(index).html()),
ImpactDegree: parseInt($(".derece option:selected").eq(index).html()),
Description: $(".diseaseMixDesc input").eq(index).val()
});
})
$("#productList").find("tbody tr").each(function (index) {
mix.MixProducts.push({
MixID: parseInt(MixID),
ProductID: parseInt($(".productid").eq(index).html()),
MeasureTypeID: parseInt($(".birim option:selected").eq(index).val()),
MeasureAmount: $(".measureAmount input").eq(index).val()
});
})
and end of this process, here is a sample json object that is post.
{
"MixName": "asdasddas",
"MixDesc": "<p>sadasd</p>",
"Price": "123",
"DiseaseMixs": [{
"MixID": 1,
"DiseaseID": 2,
"ImpactDegree": 5,
"Description": "asads"
}, {
"MixID": 1,
"DiseaseID": 3,
"ImpactDegree": 4,
"Description": "aqqq"
}],
"MixProducts": [{
"MixID": 1,
"ProductID": 2,
"MeasureTypeID": 3,
"MeasureAmount": "3"
}, {
"MixID": 1,
"ProductID": 3,
"MeasureTypeID": 2,
"MeasureAmount": "45"
}]
}
ajax post
$.ajax({
url: 'SaveMix',
type: 'POST',
data: JSON.stringify(mix),
contentType: 'application/json; charset=utf-8',
success: function (result) {
console.log(result);
},
error: function (xhr, status) {
alert(status);
console.log(xhr);
}
})
and MVC Model and JSONResult function
Model
public class MixModel
{
public string MixName { get; set; }
public string MixDesc { get; set; }
public float Price { get; set; }
DiseaseMix[] DiseaseMixs { get; set; } //DiseaseMix EntityFramework entity
MixProduct[] MixProducts { get; set; } //MixProduct EF
}
function
[HttpPost]
public JsonResult SaveMix(MixModel mix)
{
bool result = false;
//do something
return Json(new { result = result }, JsonRequestBehavior.AllowGet);
}
and here is the result I get is.
No matter how I tried, I could not bind the model.
What am I doing wrong? Please give me some help.
Thanks in advance.
Model binding is failing because those 2 properties are currently private(which is the default when you don't specify anything explicitly).
Change the 2 properties to public so that model binder can set the values of those.
public class MixModel
{
public string MixName { get; set; }
public string MixDesc { get; set; }
public float Price { get; set; }
public DiseaseMix[] DiseaseMixs { get; set; } //DiseaseMix EntityFramework entity
public MixProduct[] MixProducts { get; set; } //MixProduct EF
}
I also suggests to not mix your view models with entities generated by your ORM. That creates a tightly coupled solution.

Nancy with Complex object using JSON not working

Client code:
var basket = {
products: [],
user: { name: "schugh" }
};
$("#basket table tr").each(function (index, item) {
var product = $(item).data('product');
if (product) {
basket.products.push(product);
}
});
$.ajax({
url: "http://localhost:12116/basketrequest/1",
async: true,
cache: false,
type: 'POST',
data: JSON.stringify(basket),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (result) {
alert(result);
},
error: function (jqXHR, exception) {
alert(exception);
}
});
Server code:
Post["/basketrequest/{id}"] = parameters =>
{
var basketRequest = this.Bind(); //basketrequest is null
return Response.AsJson(basketRequest , HttpStatusCode.OK);
};
Other model classes:
[Serializable]
public class BasketRequest
{
public User User;
public List<Product> Products;
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public ProductStatus ProductStatus { get; set; }
}
public enum ProductStatus
{
Created,
CheckedBy,
Published
}
public class User
{
public string Name { get; set; }
}
The code in the Nancy Module this.Bind(); returns null. If I change the Complex object to just List<Product>, i.e. with no wrapper BasketRequest, the object is fine...
Any pointers?
EDIT: JSON posted:
{
"User": {
"Name": "SChugh"
},
"Products": [{
"Id": 1,
"Name": "Tomato Soup",
"Category": "Groceries",
"Price": 1
}, {
"Id": 2,
"Name": "Yo-yo",
"Category": "Toys",
"Price": 3.75
}]
}
Your BasketRequest object should implement properties instead of fields. So
public class BasketRequest
{
public User User { get; set; }
public List<Product> Products { get; set; }
}
also you should probably use the generic method too
this.Bind<BasketRequest>();

Resources