EF6.1 Code First: 1:1 or zero issue with adding records and foreign keys - ef-code-first

I have two entities, Executive and User. A User can be an Executive but an Executive HAS to be a User.
Executive:
public class Executive
{
[Key]
public int ExecutiveId { get; set; }
// every executive is a user
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }...and so on
User:
public class User
{
[Key]
public int UserId { get; set; }
public int? ExecutiveId { get; set; }
[ForeignKey("ExecutiveId")]
public virtual Executive Executive { get; set; }... and so on (note the nullable ExecutiveId)
CMSContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Executive>()
.HasRequired(x => x.User)
.WithOptional(s => s.Executive)
.WillCascadeOnDelete(false); ... and so on
So, I added two Users and one executive in the initializer and one Executive like this:
var users = new List<User>
{
new User{ EmailAddress="a#a.com", LastName="D'Amore", FirstName="Beau", PhoneNumber="888-555-1212"},
new User{ EmailAddress="b#b.com", LastName="Munster", FirstName="Herman", PhoneNumber="123-555-7878"}
};
users.ForEach(s => context.Users.Add(s));
context.SaveChanges();
Executive exec = new Executive();
exec.ExecutiveBlurb = "test blurb";
exec.ExecutiveTitle = "President";
exec.ProfilePictureContent = new Content { ContentBytes = File.ReadAllBytes("c:\\exec.jpg"), AddedDate = DateTime.Now, AddedByUserId = 1 };
exec.ProfileSidePictureContent = new Content { ContentBytes = File.ReadAllBytes("c:\\exec2.jpg"), AddedDate = DateTime.Now, AddedByUserId = 1 };
exec.UserId = 1;
exec.User = users.Where(x => x.UserId == 1).First();
exec.ExecutiveSections = context.ExecutiveSections.Where(x => x.SectionName == "Executive Team" && x.SectionName == "Acquisitions").ToList();
context.Executives.Add(exec);
Thing is, the Executive is created after the User (as it should be since it's 1:1 or 0) But the User never gets the ExecutiveId set... doesn't EF handle the foreign keys automagically? Like, when I add objectA to objectB, normally, setting A to have B as it's foreign key sets B to have A as it's reciprocating property... I'm missing something with the 1:1 or 0 thing.
any advice?

When using Entity Framework with a 1:0..1 relationship, or any other kind of one-to-one relationship, both entities on each side of the relationship must share a primary key. For whichever side is dependent -- with required-optional, it would be the optional side -- the primary key property is also the foreign key.
In your case, you have some conflicting/redundant annotations and fluent API calls, but fluent API takes precedence, so Executive.UserId and User.ExecutiveId are being ignored. The Executive you created should end up with exec.User.UserId == exec.ExecutiveId.
This makes sense because you'll never have an Executive without a User, and there will never be more than one Executive tied to the same User, so an Executive wouldn't need a separate primary key.
https://msdn.microsoft.com/en-us/data/jj591620#RequiredToOptional

Related

DocumentDb Repository to Query Child Documents Generically

In DocumentDb, is it possible to search for child documents that meet a certain criteria without having to involve the parent class in the query?
BACKGROUND
I'm (trying to start by) using the DocumentDbRepository.cs that is generated for you automatically in the Azure Portal when you create a new Azure Cosmos DB account. However, it's obvious that this was meant merely as a starting point and will require some additional work for individual scenarios.
In a C# Console app (.NET Core) I have a simple parent-child relationship between Company and Employees:
public class Customer
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "location")]
public string Location { get; set; }
[JsonProperty(PropertyName = "employees")]
public List<Employee> Employees { get; set; }
public Customer()
{
Employees = new List<Employee>();
}
}
public class Employee
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "firstName")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "lastName")]
public string LastName { get; set; }
[JsonProperty(PropertyName = "sales")]
public double Sales { get; set; }
}
In the Document Explorer, I can see that I have one instance of this class structure like so:
{
"id": "7",
"name": "ACME Corp",
"location": "New York",
"employees": [
{
"id": "c4202793-da55-4324-88c9-b9c9fe8f4b6c",
"firstName": "John",
"lastName": "Smith",
"sales": 123
}
]
}
If I wanted to get all Companies that meet a certain criteria, it would be a fairly easy operation using the generated DocumentDbRepository.cs methods:
DocumentDBRepository<Customer>.Initialize();
var customers = DocumentDBRepository<Customer>.GetItemsAsync(p => p.Location.Equals("New York")).Result;
... for reference, the generated GetItemsAsync() from Microsoft method looks like this:
public static async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
PROBLEM
HOWEVER, if I want to retrieve ONLY EMPLOYEES regardless of the Company they belong to, I'm not sure how to write a method in the repository class that will accomplish this.
First, I think I'll need some sort of type property so I can differentiate what a Customer is versus an Employee (versus other domain class types I may want to also add in the same collection).
Second, I would probably query that using that type property for all queries and not use the DocumentDbRepository.cs methods which seem to only work with root data. In other words, the DocumentDbRepository.cs methods seem to only be concerned with non-hierarchical entities.
But this is where things break down ... given the generic nature of this sample repository class, I can't quite connect the dots in my mind required to query sub-documents / children.
I am merely asking for a nudge in the right direction here. Thank you.
I want to retrieve ONLY EMPLOYEES regardless of the Company
If I understanding correctly, you want to query employees according employees' property from Customer. If it is that case, we could do that with SQL as following, and we just need to change the where <filter_condition> as we want.
SELECT c as employees
FROM c IN Customer.employees
WHERE c.firstName = 'John'
I test with your mentioned document from Azure portal, it works correctly on my side.
The following is the c# demo code:
var endpointUrl = "https://yourdocumentdbname.documents.azure.com:443/";
var authorizationKey = "xxxxx";
var databaseId = "database name";
var collectionId = "collecton name";
var client = new DocumentClient(new Uri(endpointUrl), authorizationKey);
var sql = "SELECT c as employee FROM c IN Customer.employees WHERE c.firstName = 'xx'";
var collection = client.CreateDocumentCollectionIfNotExistsAsync(
UriFactory.CreateDatabaseUri(databaseId), new DocumentCollection
{
Id = collectionId
}).Result.Resource;
var query = client.CreateDocumentQuery(collection.SelfLink, sql).AsDocumentQuery();
while (query.HasMoreResults)
{
var documents = query.ExecuteNextAsync().Result;
//do something
}

ASP.NET MVC: How to Insert Data Into Multiple Tables?

I have two tables (NPG_Chemical and NPG_Chemical_Synonym):
public partial class NPG_Chemical
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(256)]
public string Chemical { get; set; }
}
public partial class NPG_Chemical_Synonym
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_Synonym_ID { get; set; }
[ForeignKey("NPG_Chemical_ID")]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(512)]
public string Synonym { get; set; }
}
In the NPG_ChemicalController I have something like:
[HttpPost]
public ActionResult Create(NPG_ChemicalViewModel model)
{
using (var context = new NPG_Model())
{
var chemical = new NPG_Chemical();
chemical.Chemical = model.NPG_Chemical.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.NPG_Chemical_Synonym.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
context.SaveChanges();
}
return View();
}
and NPG_ChemicalViewModel:
namespace NPG_Administrative_Utility.Models
{
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
}
When I try to create a view based on NPG_ChemicalViewModel, it shows:
Can any one help me on this?
You'll need a view model. At the simplest, you can just do something like:
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
Then, change your action to accept this:
public ActionResult Create(NPG_ChemicalViewModel model)
In your view, you would generate the individual properties like:
#Html.EditorFor(m => m.NPG_Chemical.Chemical)
However, it's far better to only include the properties on your view model that you want to be edited:
public class ChemicalViewModel
{
public string Chemical { get; set; }
public string Synonym { get; set; }
}
Then, in your action, you just map this posted data where it should go:
var chemical = new NPG_Chemical();
chemical.Chemical = model.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
That said, there's some significant issues with your code here. First, it looks like you're dealing with a one-to-one or one-to-many relationship here between Chemical and Synonym, but right now, you have no foreign keys being utilized. You should add a navigation property to your synonym class:
[ForeignKey("NPG_Chemical_ID")]
public NPG_Chemical Chemical { get; set; }
That tells Entity Framework that you have a relationship and among other things allows it to automatically fill in IDs as necessary. For example, with that, you could now simply do:
synonym.Chemical = chemical;
Instead of directly referencing the ID. That way, if the id is autogenerated or otherwise unknown before saving, the relationship will still be preserved. Whereas, without it, you'd have to save chemical first, set the autogenerated id on synonym and then save the synonym in a separate transaction.
Second, if you're going to use keys typed as "numeric". Then, you're going to be responsible for generating a unique numeric string for each record. That's a huge pain, as it's going to require checking a proposed id against other existing record ids before actually saving. Otherwise, you run the risk of a primary key collision. It's far better to use a standard autoincrementing PK or barring that, at least a GUID, where you're assured a reasonably low risk of collisions occurring.
Third, you should absolute not use using with your context. Here it's not a big deal, since, you're only saving and not reading data from the database, but in a typical view, lazy-loading will kick you in the posterior quick doing that. Your context should be request-scoped, either as an instance variable on your controller (since the controller is newed up and disposed with each request) or using dependency injection. You never want to create an instance of your context anywhere else, including an action method.

NHibernate - one-to-many just not working with SQLite

TL;DR;
NHibernate reverse relationship is working on Azure-SQL and MSSQL2012 but not with SQLite
Description:
I am currently Unittesting my Asp.Net MVC App and set up my Unittest with FluentMigrator on SQLite.
After creating the Database I set up some base entries I need.
One of those is a Product.
A Product has many ProductSuppliers and a ProductSupplier has many ProductSupplierPrices
public class Product
{
public virtual long Id { get; set; }
public virtual string Number { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
//more properties
public virtual IList<ProductSupplier> Suppliers { get; set; }
//more properties
}
public class ProductSupplier
{
public virtual long Id { get; set; }
public virtual Product Product { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual IList<ProductSupplierPrice> Prices { get; set; }
}
public class ProductSupplierPrice : IHaveId
{
public virtual long Id { get; set; }
public virtual ProductSupplier ProductSupplier { get; set; }
public virtual decimal FromAmount { get; set; }
public virtual decimal Price { get; set; }
}
Setup:
Create Supplier
Create Product
Create ProductSupplier
Create ProductSupplierPrice
Test:
Product product = this.session.Load<Product>((long)1);
ProductSupplier productSupplier = product.Suppliers.First(); //<-- Suppliers are null; therefore throws an exception
If I load them seperately to check the relationships:
productSupplierPrice.ProductSupplier <--- Correct Supplier
productSupplier.Prices <-- Null
productSupplier.Product <-- Product with Id 1
product.Suppliers <-- Null
So to me it seems, that the many-to-one direction works correctely, but the one-to-many (reverse relation) is not working.
The Problem exists only in my Unittest (SQLite) the App itself runs on Azure-SQL and is working fine.
EDIT:
Mappings with FluentnHibernate
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
HasMany(x => x.Suppliers).Inverse().Cascade.DeleteOrphan().BatchSize(20);
//many more mappings
}
}
public ProductSupplierMap()
{
Id(x => x.Id);
References(x => x.Product);
References(x => x.Supplier);
Map(x => x.IsMainSupplier);
Map(x => x.SupplierProductNumber);
Map(x => x.CopperSurcharge);
HasMany(x => x.Prices).Inverse().Cascade.DeleteOrphan().BatchSize(20);
}
public ProductSupplierPriceMap()
{
Id(x => x.Id);
References(x => x.ProductSupplier);
Map(x => x.FromAmount);
Map(x => x.Price);
}
Edit2 - Creating the DB-Entries:
Product product = new Product()
{
Type = ProductType.Purchase,
Dispatcher = session.Load<Employee>(employeeId),
Number = "100.10-1000",
Name = "Testproduct",
//Lots of Properties
Suppliers = new List<ProductSupplier>()
};
session.SaveOrUpdate(product);
ProductSupplier productSupplier = new ProductSupplier()
{
Product = product,
Supplier = session.Load<Supplier>((long)1),
IsMainSupplier = true,
SupplierProductNumber = "Artikel123456",
CopperSurcharge = CopperSurchargeType.DEL700,
Prices = new List<ProductSupplierPrice>()
};
session.Save(productSupplier);
ProductSupplierPrice productSupplierPrice = new ProductSupplierPrice()
{
ProductSupplier = productSupplier,
FromAmount = 1,
Price = 5
};
session.Save(productSupplierPrice);
EDIT 3.1:
public static ISession InitializeDatabase()
{
NHibernateSessionHolder.CreateSessionFactory();
session = NHibernateSessionHolder.OpenSession();
CreateBaseEntries(); //Creates Employees, Supplier, Customer etc
return session;
}
Based on the Ayende's article you need to clear the session between insert/update and querying:
session.Clear();
Seems to be a session management, I'm not sure why the session should be clean, but the session is providing your original instance (the same you provided for saving, stored on the session cache) instead a proxy for lazy-loading.
private long CreatePurchaseOrder()
{
session.Clear();
var product = this.session.Load<Product>((long)1);
var productSupplier = product.Suppliers.First();
var productSupplierPrice = productSupplier.Prices.First();
return 0;
}
Sorry for late reply
In your unit test, you are using same session for creating and fetching entities. This is not right as subsequent fetch returns entities from first level cache which do not have their graph set up properly.
So....either use different sessions OR as a quick fix, I have added "session.Clear()" in the method "InitializeDatabase()" of "DatabaseSetUpHelper". Clearing the session clears first level cache and force NH to fetch data from DB again and the resulting entities have their graph set up properly.
public static ISession InitializeDatabase()
{
NHibernateSessionHolder.CreateSessionFactory();
session = NHibernateSessionHolder.OpenSession();
CreateBaseEntries();
session.Clear(); // notice this!!! this clears first level cache of session, thus forcing fetching of data from DB
return session;
}
Note: My quick-fix is not final solution, it is there just show how session behaves. In proper solution, you must use different sessions.

Entity Framework modify models

I'm trying to modify the model entity which is sent to the CreateEmployee Method as a parameter to be modified.
public void CreateEmployee(string roleName, EmployeeModel emp)
{
string roleName == "user";
emp.Roles.Select(e => new RoleModel { RoleName = roleName });
AddEmployee(emp);
}
this is how models looks like....
And it give me an error saying
Model does not contain a definition for 'Select' and 'Select' accepting a first argument of type Model could be found....
I have tried using the 'Where' method as well, but still give the same error..
emp.Roles.Where(e => e.RoleName == roleName)
You have some errors in your code,
use = not == for assignment
the selected roles does not used
Linq query functions like Select and Where apply to IEnumerable the Roles property is of type RoleModel not IEnumerable<RoleModel>
so:
public class EmployeeModel
{
//some code
public ICollection<RoleModel> Roles{ get; set; }
}
using System.Linq;
public void CreateEmployee(string roleName, EmployeeModel emp)
{
string roleName = "user";
var empRoles = emp.Roles.Select(e => new RoleModel { RoleName = roleName });
AddEmployee(emp);
}
Edit:
If you want to have one role per employee at most, the relationship between Role and Employee became one-to-many(each role has n employee)
if Role is optional for employee:
public class EmployeeModel
{
//some code
public short? RoleId { get; set; }//nullable foreign key
public virtual RoleModel { get; set; } //Navigation property
}
public class RoleModel
{
//some code
public ICollection<Employee> Employees{ get; set; }
}
But, if Role is required for Employee you must change the foreign key to:
public short RoleId { get; set; }//non-null foreign key
For getting an Employee's Role you do not need to use Select or Where on navigation property, just use employee.Role.
Finally for querying Role's employees you can use Select, Where, ... as mentioned before.

Only return selected fields in Web API results

First of all, this is not exactly a duplication of the dozens of other posts and I have tried all of them and none of them work.
I have a model that contains many more values than my web api consumers need.
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
public DateTime CreatedDt { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDt { get; set; }
public string UpdatedBy { get; set; }
}
I only want say a few of the fields to be passed in the API. I've tried this code but instead of leaving out say UpdateBy in the Json result, it returns it with a null value. How do I get rid of that? I've tried several dozen variations but they either fail to compile or fail to return results.
public IQueryable<Publication> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new Publication {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
Don't serialize your DAO. Create a complete contract and then serialize it selectively. To creating different contracts for different cases, you could simplify it using Json.Net; you could just create a custom contract resolver and use it as a parameter of SerializeObject() like so
static void Main(string[] args)
{
var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36};
var firstNameContract = new SelectiveSerializer("firstname");
var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age");
var allJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = allPropertiesContract});
var firstNameJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = firstNameContract});
Console.WriteLine(allJson);
// {
// "FirstName": "John",
// "LastName": "Doe",
// "Age": 36
// }
Console.WriteLine(firstNameJson);
// {
// "FirstName": "John",
// }
}
public class SelectiveSerializer : DefaultContractResolver
{
private readonly string[] _fields;
public SelectiveSerializer(string fields)
{
var fieldColl = fields.Split(',');
_fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower());
return property;
}
}
public class TestContract
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Without much effort, you could probably work this into your default mediatype formatter (in the pipeline) to look for a parameter in the request called 'fields' or whatever and then use the custom contract resolver if present, and then it would be seamless default behavior to limit fields if specified or serialize the entire object if not specified.
On the academic side, here is the justification:
Any modification to the data is considered a "view concern" which means, in an API, it should controlled by query parameters and accept header. In this case, the "representation" of the data is application/json and you've chose to "filter" the returned fields. All of this can (and should be, imo) be handled during serialization. So your "model" in this case will always be the full model vs. some subset of the model. The full model in this example contains first name, last name, and age. In reality, this could be hundreds of properties. If you want to allow the client to choose a subset of the complete model, this is how you could do it with selective serialization.
You can similar behaviors in graph apis. There, the default for large models is that you get an empty object if you don't specify fields, forcing the client to be very specific about what it asks for, which is great when payload size matters (e.g. mobile applications). And, there's nothing stopping from creating field presets like 'name' which could mean 'firstname, lastname' or 'all' which includes all properties.
I've never been a fan of having hundreds of data objects that all serve some ad hoc requirement for a data set that is used in 20 different contexts where some cases require more data while others require less. IMO if you have to go through the same process to get the data, whether it complete or not, you shouldn't waste your time creating additional objects to frame the data for the sake of the client, and this should help you achieve that.
It's because you're returning a collection of Publication objects so you will get every property that is contained in that class, whether you populate it or not. If you want to return a subset of the properties then create a class that has only the properties you want to return and create an instance of that class in your query.
public IQueryable<WhatIReallyWantToReturn> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new WhatIReallyWantToReturn {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
private class WhatIReallyWantToReturn
{
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
}
using Newtonsoft.Json;
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
[JsonIgnore]
public DateTime CreatedDt { get; set; }
[JsonIgnore]
public string CreatedBy { get; set; }
[JsonIgnore]
public DateTime UpdatedDt { get; set; }
[JsonIgnore]
public string UpdatedBy { get; set; }
}
as Craig W. said you can use viewmodel ,also you can use anonymous type
(notice viewmodel is better way because you can use some utilities like automapper for mapping your property automatically)
JsonIgnore annotation has worked for me
[JsonIgnore]
public int Ranking { get; set; }
Here is a great article (Dec 2019) on the subject. It offers a solution for data shaping by making use of ExpandoObject and Type Reflection. The properties that the client requires can then be passed through the request as a query parameter (i.e. separated by a comma). The article also offers solution to the JSON Serialization problem.
Startup.cs file:
services.AddControllers(config =>
{
config.RespectBrowserAcceptHeader = true;
config.ReturnHttpNotAcceptable = true;
})
.AddXmlDataContractSerializerFormatters()
.AddNewtonsoftJson();
+1 for Sinaesthetic's answer.
I just finished reading an article, about GraphQL which solves exactly this problem. You can define exactly which fields do you need in the same request. No need for creating new endpoints every single time, when the caller needs just a specific subset of the properties.
If you can do this in .NET WEB API too without creating new models and endpoints, with just a very little extra effort, why wouldn't you (instead of exchanging Web Api for GraphQL).
Actually his SelectiveSerializer could be upgarded with reflection, so if you want to define which props you need in
C#, you can do this by providing property expressions, so you don't have to worry about misstyping prop names.
I bet there are other solutions for this, but the basic concept is the most important that we can define which fields we need in our json without creating new models.

Resources