HotChocolate merge entity filter - c#

I use for my ASP.NET Web API HotChocolate as a GraphQL Server, which generates filter based on the Entity used on the Query.
The project use an old DB schema, which is way outdated and can't/shouldn't be used anymore, therefore, we want to slowly migrate to a new one.
So to my question:
Is it possible to create a GraphQL filter based on two or multiple Entities from different DB contexts?
Example:
class ExampleOld {
public string Information1 {get; set;}
public ChildOld ChildOld {get; set;}
}
class ChildOld {
public string Text1 {get; set;}
}
class ExampleNew {
public string Information2 {get; set;}
public ChildNew ChildNew {get; set;}
}
class ChildNew {
public string Text2 {get; set;}
}
Query:
query {
example(where: {
and: [
{information1: {eq: '...' }}
{childOld: { text1: {eq: '...'}}}
{information2: {eq: '...' }}
{childNew: { text1: {eq: '...'}}}
}]) {
...
}
}
For now I set the filter manualy on the IQueryable object with the IResolverContext.
Tec Stack:
.NET 6.0
EF Core
HotChocolate
MSSQL DB (for old a new DB)
I'm also open to other solutions.
One solution with thought of, was to migrate all data/table/columns we use now and only work with the new DB.
If we start with a new feature, then we have to migrate the connected tables first.
The problem is on this solution, that we have to migrate all data that we want to filter with.

Related

Can I get return type from stored procedure without creating DbSet in EF Core?

Supposedly, I have a simple DbContext with Blog and Post models:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Let's say I have a stored procedure that returns some DTO:
[Keyless]
public class BlogPostDto
{
public string PostTitle { get; init; }
public string BlogName { get; init; }
}
Today I put the following into DbContext:
public class AppDbContext : DbContext {
public virtual DbSet<BlogPostDto> NeverUseIt { get; set; }
partial void OnModelCreatingPartial(ModelBuilder modelBuilder) {
modelBuilder.Entity<BlogPostDto>().ToView(null);
}
}
And then I can get Stored Procedure results shaped in the way I want:
List<BlogPostDto> results = await db.Set<BlogPostDto>().FromSqlRaw($"EXEC MyProc").ToListAsync();
So, my question is, do I have to add my BlogPostDto into DbContext? I know that in EF Core 3 I did; but there were a large number of improvements since then. Creating a bogus DbSet and mapping it to non-existent view just feels counter-intuitive!
The closest I found in most current documentation is here. The very first example of context is Serving as the return type for raw SQL queries. - but the article assumes that I have a matching view already in the database.
UPDATE: It looks like ToView(null) is not necessary - just DbSet<>
Nothing has changed in that regard so far from what you see in the EF Core 6.0 documentation and SO posts you are referring to.
Just to be crystal clear, you don't need a DbSet<T> returning property in your context. But you do need to include the type (keyless or not) in the model using the modelBuilder.Entity<T>() call, and also ToView(null) to prevent EF Core migrations associate database table and/or view with it, and optionally HasNoKey() in case you don;t want to use EF Core dependent attributes like [Keyless] in your data classes.
So the minimum requirement for your example is this line
modelBuilder.Entity<BlogPostDto>().ToView(null);
Now, this is a long time requested feature (which exists in the "obsolete" EF6 which the "modern" EF Core is supposed to replace), tracked by Support raw SQL queries without defining an entity type for the result #10753 issue in EF Core issue tracker. It was initially planned to be included in the upcoming EF Core 7.0 release (Nov 2022), but later has been cut for (eventually) EF Core 8.0 (Nov 2023). So until then you have to use the "register model" approach, or use 3rd party library(!) like Dapper for the same task, as suggested by one of the EF Core team members(?!).

Adding Complex Objects in Entity Framework

I'm setting up .NET Core API. My application is divided into 3 layers:
Data Access
Business Logic/Services
API
I'm having troubles added related objects with the API method. Let's say I have following classes.
public class Part
{
[Key]public int Id {get; set;}
public string Name {get; set;}
public string ICollection<PartAttribute> PartAttributes{get; set;}
}
public class PartAttribute
{
[Key]public int Id {get; set;}
public string Name {get; set;}
public string Value {get; set;}
}
And following methods for interaction with DB - context is EF DbContext:
public virtual void Add(T entity)
{
Context.Set<T>()
.Add(entity);
}
I'm having trouble adding Part that have already existing PartAttributes, if I send following JSON via API (assuming that following record in PartAttributes already exist)
{
"Name":"Example1",
"PartAttributes":[
{
"attributeId":1,
"name":"Color",
"value":"black"
}]
}
I'm getting a following exception: "Cannot insert explicit value for identity column in table 'PartAttributes' when IDENTITY_INSERT is set to OFF." - This leads me to conclusion that EF is not recognizing the existing record and tries to insert it as new one. This results in fail because of identity insert setting in SQL Server itself.
What I'd like to achieve is that the existing objects would be recognized and EF would not try to insert the existing attributes as new records in the database. What are the best practices to achieve that behaviour?
You need to take your primary key (which here looks to be attributeId) and load the record using the dbContext. Once loaded update the values and call SaveChanges() or SaveChangesAsync() on the context.
You can even have a method that does insert or update.
for example (SUDO CODE!):
public void InsertOrUpdate(PartAttribute model)
{
var existingRecord = dbContext.Set<PartAttribute>().Find(model.attributeId);
if(existingRecord == null)
{ // Insert (Should not include value for attributeId)
dbContext.Add(model);
}
else
{ // Update
existingRecord = model;
dbContext.SaveChanges();
}
}
Please let me know if you still have issues

Read and Write to database with generic DataTable using Entity Framework

Is it possible to read and write to a SQL Server database using DataTable with Entity Framework?
I have multiple code tables defined in my database such that each of them share a fixed set of properties as shown in the sample below.
For example
public class CTGender
{
public Guid ID { get; set; }
public string Description { get; set; }
public string DisplayValue { get; set; }
//...Other properties specific to CTGender
}
public class CTNationality
{
public Guid ID { get; set; }
public string Description { get; set; }
public string DisplayValue { get; set; }
//...Other properties specific to CTNationality
}
The situation I face right now is the ever expansion of my code tables, could be another CTCountry, CTRole and so on, for example.
I am trying to synchronise these code tables between multiple databases.
The solution is heavily dependent on Entity Framework as the data access.
Is there a generic way for Entity Framework to read and write ALL these code tables without their entity models defined, like how you can read and write generic DataTables using ADO.NET?
Yes, there are couple of ways by which you can create tables at code side then either using code first approach or using publish project mechanism you can generate tables in SQL server using entity framework.
In the latter approach, you can create a separate project where you can write SQL for your various tables. This project should target SQL Server. You can right click on this project and click on publish option for updating all your tables inside SQL server.

Entity Framework: Create and update related objects in WPF

I'm using EF 6.0 and code-first approach.
I have problem with create and update data in db via Entity Framework. I'm not sure if I need to make db.Groups.Attach(student.Group) before storing Student. Without this after saving Student I also have new Group with the same Name but other GroupId.
Moreover I can't update student because I'm getting exception: The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
public class Student {
[Key]
public int StudentId {get; set;}
public string Name {get; set;}
public Group Group {get; set;}
}
public class Group {
[Key]
public int GroupId{ get; set;}
public string Name {get; set;}
public virtual ICollection<Student> Students {get; set;}
}
.
public class StudentDao {
public void createStudent(Student student) {
using (var db = new StorageContext()) {
// without this also creates new Group.
db.Groups.Attach(student.Group);
db.Students.Add(student);
db.SaveChanges();
}
}
public void updateStudent(Student student) {
using (var db = new StorageContext()) {
var original = db.Students.Find(student.StudentId);
if (original != null) {
original.Name = student.Name;
original.Group = student.Group;
db.SaveChanges(); //exception
}
}
}
}
Without this (db.Groups.Attach(student.Group)) after saving Student I also have new Group
That's because Adding an entity to a DbSet marks all adhering entities that are not yet tracked by the context as Added. This is an EF feature, like it or not, so you have to first attach the entities you don't want to re-insert.
Moreover I can't update student because I'm getting exception
For some reason, in the update method the student and its group are still attached to a context. Apparently, there is some other context active in the StudentDao class. You have to make sure this context's lifespan is over when you update the student or else (second best) detach the student and the group from it.
An off-topic advice: if you can, abandon this DAO pattern. EF works much better when you use the DbContext and its DbSets in service-like methods that handle one unit of work. With these DAO's it's impossible to work transactionally and they cause piles of repeated code.
Been a while since I worked on Entity but from what I remember you can't just change the Group, you have to give it a GroupID field as well, change that instead and then reload/update the Student object from the database so that the Group object gets loaded and assigned from within the same context.
This is just one of the reasons I use NHibernate.

Elegant way to map an DTO object graph back to an Entity Framework object graph in a Client/Server app

I have a client/server application where the server uses Entity Framework as the ORM.
Every entity that is to be sent to the client is represented by a DTO-class.
The mapping between the Entity Framework and DTO-classes is handled using AutoMapper.
Let's say we have the following Tables:
Person (string Name, int CountryID)
Country (int CountryID, int Population, string Name)
They are represented by the following EF classes:
class Person
{
public string Name { get; set; }
public int CountryID { get; set; }
public Country Country { get; set;}
}
class Country
{
public int CountryID { get; set; }
public int Population { get; set; }
public string Name { get; set;}
}
Which in turn are represented by the following DTOs:
class PersonDTO
{
public string Name { get; set; }
public CountryDTO Country { get; set;}
}
class CountryDTO
{
public int CountryID { get; set; }
public int Population { get; set; }
public string Name { get; set;}
}
The initial state of the database represents an empty Person-Table and a Country-table that has one entry: (1, 123, 'CountryXYZ')
The clients' app task is to create a new Person Entity and attach its Country-Reference to the available 'CountryXYZ' Country-Entity.
In order to do so, the client app first requests the available CountryDTOs.
It then creates a new PersonDTO instance and sets its Country Property to the only CountryDTO that it has received from the server.
This PersonDTO-instance is then being sent back to the server.
The server in turn maps the PersonDTO-instance back to a Person-instance.
The last servers step is now to store the Person-instance in the ObjectContext and call ObjectContext.SaveChanges().
The problem I have with this approach is that as soon as I call ObjectContext.SaveChanges(), a new Country-row is created in the database instead of just using the available Country-row.
What am I missing here?
I am new to EF and I think this use case is pretty common... so I hope there is an easy fix to this.
In case the problem description is not clear enough please let me know.
Thanks!
If you know that client will always use country it already received from your server (it is existing one) you can simply modify your saving logic to use:
objectContext.PersonSet.AddObject(personToSave);
objectContext.ObjectStateManager
.ChangeObjectState(personToSave.Country, EntityState.Unchanged);
objectContext.SaveChanges();
If you use AddObject method the entity and all its relations are marked as added and will be inserted to the database as new objects unless you reconfigure their state.
Your entity also exposes FK property so you can use FK property when you map your DTO back to entity instead of creating country instance. In such case you will not need to deal with changing the state of relation because that relation will be represented only through integer column.
If client can create both Person and Country in single call you will need some flag in your DTO to differ between existing or new entity or you will have to query database to verify if such Country already exists.
This is an instance where using the Self Tracking Entities would probably be useful to you.
It uses a T4 template to generate your entity classes, and they can be round-tripped across the wire using WCF. However, you need to share the assembly that contains the entities on the client and the server.
If you are in control of both, and you are using .Net for both, I would go that route.

Categories