Git Product home page Git Product logo

graphql.entityframework's Introduction

graphql.entityframework's People

Contributors

actions-user avatar andyedinborough avatar apocdev avatar asiffermann avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar elertan avatar gbiellem avatar isabekyan avatar jurion avatar kevinkeillor avatar mlynam avatar monkeywithacupcake avatar nze avatar onionhammer avatar phated avatar schernyh avatar shane32 avatar simoncropp avatar slgshark6 avatar techniq avatar valdisthomann avatar vodesoft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql.entityframework's Issues

Clarify what "No null checking of nested values is done." means

The phrase "No null checking of nested values is done" is present in the readme, when describing "where" queries. It is not obvious what this means.

I can think of multiple interpretations:

  • An intermediate value being null means the query will explode with an exception.
  • An intermediate value being null means the query will treat the child value as null.
  • An intermediate value being null means the query will mean the row never matches.

Possibly there are even more interpretations. Please clarify what the actual behavior is.

Comparison to OData

Hi Simon,

thanks for all the help.

1 question: Do you see any disadvantages compared to Odata when using graphQL with your library?
this is a cheat sheet showing the OData features.
Maybe only count would be missing here..

Add 'arguments' to AddSingleField

Suggestion: Although QueryGraphType.AddQueryField has an arguments parameter, AddSingleField does not. I would like to suggest that this feature be added. This will be useful because I want to implement the mutation class as a QueryGraphType, utilizing AddSingleField to allow the return type to provide potentially the entire graph (including subtables) of the updated data. So it would be coded like any other mutation, except the last line would be return dbContext.MyTable.Where(x => x.Id == argumentModel.Id). This would have another benefit of not having to provide separate return graphs/models for mutations, as they can simply return the same graph/model as the query. Of course, another call to the database would be made for every update, but this could be viewed as a benefit, and would be necessary anyway if subtables were requested.

Is there a way to express ISNULL?

As the title says, I'd like to be able to pass an ISNULL through to SQL. In normal EF I'd probably do something like .Where(i => i.Property1 ?? i.Property2), I don't see a way to put the null coalescing operator into a GQL query.

add sql server query logging

Hi Simon,

I want to add sql logging to log sqlCommands executed.

You use
var myDataContext = InMemoryContextBuilder.Build<MyDataContext>();
in my other project i have this
services.AddDbContext<DbContextOrderStatuses>(options => options.UseLoggerFactory(factory).UseSqlServer(orderStatusConnString));
how can i access the DbContextOptionsBuilders ? Or should i dump InMemoryContextBuilder and use my own?

thanks

Support Group By / Aggregated values

I'm currently evaluating moving from an OData backend to GraphQL and was trying to find the best approach to retrieve aggregated values from a GraphQL endpoint. I've been looking at your project and it looks to solve a most of the non-aggregated data access I need (thank you), but I wasn't sure if retrieving aggregated values was within the scope of the project or the best approach to take.

In general, I've seen retrieving aggregated values within GraphQL is outside the scope of the GraphQL spec directly, but have seen some support and proposals to enable something like:

query PostData {
  viewer {
    allArticles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
      aggregations {
        count
        sum {
          recommends
          reads
        }
        avg {
          recommends
          reads
        }
      }
    }
  }
}

Moving from OData, this is directly supported using $apply transforms, and I've written an object-based query syntax for it that might also be used for inspiration.

My questions / proposal are:

  • Is there a suggested way to use GraphQL.EntityFramework to expose aggregated values (customize the resolve query in a AddQueryField for example)?
  • Would supporting aggregations in a standard way be considered for this project (via a syntax similar to that above)?

Custom Entity Key

According to the documentation "Currently the only supported identity member (property or field) name is Id."

Just in case, are [Key] annotations supported for specifying the entity key property? If not, any hints on what would be required to change?

Thanks.

Resolving Query Connection Fields Makes Suboptimal Database Access

I was evaluating this project for an upcoming project and found some places where suboptimal database access was being made.

Consider a simple setup where (omitting a lot of boilerplate code):

    public class MyContext : DbContext
    {
        public DbSet<MyItem> Items { get; set; }
    }

    public class MyItem
    {
        public int Id { get; set; }
        public string A { get; set; }
        public string B { get; set; }
        public string C { get; set; }
    }

    public class MyQuery : EfObjectGraphType
    {
        public MyQuery(IEfGraphQLService gql, MyContext db)
            : base(gql)
        {
            AddQueryConnectionField(
                name: "allItems",
                resolve: context => db.Items);
        }
    }

Performing a simple GraphQL query:

{
    allItems { totalCount }
}

Will cause the following database queries to be made by EF Core:

SELECT COUNT(*)
FROM "Items" AS "i"

SELECT "i"."Id", "i"."A", "i"."B", "i"."C"
FROM "Items" AS "i"
LIMIT @__p_1 OFFSET @__p_0

From what I gathered from looking at the source, this is because when resolving the Connection<MyItem> there is no optimization being made to not query the list of items when neither edges { node } nor items is selected. I would expect only the following database query to be made:

SELECT COUNT(*)
FROM "Items" AS "i"

In the same vein the following query will needlessly retrieve the total count (with a SELECT COUNT(*)... database query):

{
    allItems { items { id } }
}

In fact, for the aforementioned query, I would expect the only following database query to be made by EF Core:

SELECT "i"."Id"
FROM "Items" AS "i"
LIMIT @__p_1 OFFSET @__p_0

Unfortunately, however, when resolving the Connection<MyItem> the entire entity is being loaded (with tracking, .AsNoTracking() can be specified manually in the EfObjectGraphType implementation on the AddQueryConnectionField call but I imagine it is a sane default to have) instead of just the required properties. This can be achieved in EF Core with something like following, where the result will only contain the Id property set and have leave the other properties unset:

using (var context = new MyContext())
{
    var result = await db.Items
        .Select(i => new MyItem
        {
            Id = i.Id
        })
        .ToListAsync();
}

The above code would result in the following database query being generated by EF Core:

SELECT "i"."Id"
FROM "Items" AS "i"

Furthermore, this can be done programmatically with Expression with something like the following example being as far as I can tell exactly equivalent to the previous operation:

using (var context = new MyContext())
{
    var itemParameter = Expression.Parameter(typeof(MyItem), "i");
    var idProperty = typeof(MyItem).GetProperty("Id");
    var expression = Expression.Lambda<Func<MyItem, MyItem>>(
        Expression.MemberInit(
            Expression.New(typeof(MyItem)),
            Expression.Bind(
                idProperty,
                Expression.MakeMemberAccess(itemParameter, idProperty))),
        itemParameter);
    var result = await context.Items
        .Select(expression)
        .ToListAsync();
}

I have not tested this with navigation collection connections so I do not know whether it has the same issues but from quickly glancing at the code it seems to.

I did some quick and dirty benchmarking using an SQLite database (disclamer these are probably not valid or accurate benchmarks and are just for illustrative purposes - code provided in this gist). Using expressions to read only the Id property from the database is roughly 270% faster than retrieving an entire entity with tracking (what is currently being done) but only 40% faster than retrieving an entire entity without tracking (this should definitely be suggested in the documentation). However, retrieving the entire entity using expressions (so an Expression.Bind per property) is roughly 15% slower than just retrieving the entire entity untracked.

This feature would probably require quite a lot of work and is something I would not be able to commit to. I decided to just post my findings here in case someone else was interested in optimizing implementing these features or funding their development.

Non Generic AddQueryField

Is there any way to add query field and resolve the context without relying on a generic implementation.

I need to call AddQueryField and resolve the data context using non generic class DbContext which return non generic DbSet.

I have read the file EfGraphQLService_Queryable.cs and all AddQueryField overloads should at least specify the return type at compile time.

Error trying to resolve when using AddNavigationConnectionField()

Hi, I have been checking out this library and I have it working on a test project for non-connection queries.

I am getting an error using AddNavigationConnectionField() when trying to get a list of paged orders for a customer.

"GraphQL.ExecutionError: Error trying to resolve customers. ---> System.Collections.Generic.KeyNotFoundException: The given key 'System.Object' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at IncludeAppender.AddField(List`1 list, Field field, String parentPath, FieldType fieldType, List`1 parentNavigationProperties) in src\GraphQL.EntityFramework\IncludeAppender.cs:line 76
   at IncludeAppender.ProcessSubFields(List`1 list, String parentPath, ICollection`1 subFields, IComplexGraphType complexGraph, List`1 navigationProperties) in src\GraphQL.EntityFramework\IncludeAppender.cs:line 96
   at IncludeAppender.GetPaths(FieldType fieldType, ICollection`1 fields, List`1 navigationProperty) in src\GraphQL.EntityFramework\IncludeAppender.cs:line 43
   at IncludeAppender.AddIncludes[T](IQueryable`1 query, FieldType fieldType, ICollection`1 subFields, List`1 navigationProperties) in src\GraphQL.EntityFramework\IncludeAppender.cs:line 29
   at IncludeAppender.AddIncludes[TItem,TSource](IQueryable`1 query, ResolveFieldContext`1 context) in src\GraphQL.EntityFramework\IncludeAppender.cs:line 20
   at GraphQL.EntityFramework.EfGraphQLService`1.<>c__DisplayClass21_0`2.<<BuildQueryField>b__0>d.MoveNext() in src\GraphQL.EntityFramework\GraphApi\EfGraphQLService_Queryable.cs:line 82
--- End of stack trace from previous location where exception was thrown ---
   at GraphQL.Instrumentation.MiddlewareResolver.Resolve(ResolveFieldContext context)
   at GraphQL.Execution.ExecutionStrategy.ExecuteNodeAsync(ExecutionContext context, ExecutionNode node)
   --- End of inner exception stack trace ---"

When I step in to the code in ComplexGraphResolver I can see in GetOrAdd(FieldType fieldType) that when it is trying to resolve the ConnectionType then resolved.EntityType is resolved as "object".

If I use the debugger and set this to typeof(Orders) (my model type) then there is no error and my query returns correctly.

Not sure if this is an error with my config or a bug.

DataContext incorrectly passed to UserContext?

Hi Simon,

By passing the DataContext in on the UserContext, does this make it impossible to use authentication?
(such as nuget GraphQL.Authorization).
I am trying to implement JWT claims based authorisation, which needs to be able to access the claims via the UserContext but this happens to contain our db context instead.
Thoughts?

Many thanks,
Jeremy

Error when select an nullable float

When i try to select some nullable float i get this error:

{
  priceHistories(ids:"230011", take:2){
		monetary
    quantity
    priceType
    priceMin
  }
}

{
  "errors": [
    {
      "message": "GraphQL.ExecutionError: Could not find conversion from System.Single to System.Double ---> System.InvalidOperationException: Could not find conversion from System.Single to System.Double\r\n   at GraphQL.ValueConverter.GetConversion(Type valueType, Type targetType)\r\n   at GraphQL.ValueConverter.ConvertTo(Object value, Type targetType)\r\n   at GraphQL.Execution.ObjectExecutionNode.ToValue()\r\n   at System.Linq.Enumerable.SelectListIterator`2.ToArray()\r\n   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)\r\n   at GraphQL.Execution.ObjectExecutionNode.ToValue()\r\n   at GraphQL.Execution.ExecutionStrategy.ExecuteAsync(ExecutionContext context)\r\n   at GraphQL.DocumentExecuter.ExecuteAsync(ExecutionOptions options)\r\n   --- End of inner exception stack trace ---",
      "extensions": {
        "code": "INVALID_OPERATION"
      }
    }
  ]
}

The class :

    public PriceHistoryGraph(IEfGraphQLService graphQlService) : base(graphQlService)
    {
        Field(x => x.DateEnter, nullable: true);
        Field(x => x.DateUpdate, nullable: true);
        Field(x => x.Monetary, nullable: true);
        Field(x => x.Operator);
        Field(x => x.PriceBase, nullable: true);
        Field(x => x.PriceClose, nullable: true);
        Field(x => x.PriceMax, nullable: true);
        Field(x => x.PriceMin, nullable: true);
        Field(x => x.PriceOpen,nullable: true);
        Field(x => x.PriceType, nullable: true);
        Field(x => x.Quantity, nullable: true);
        Field(x => x.ReportDate, nullable: true);
        Field(x => x.Id);
}
    public partial class PriceHistory
    {
        [Column("SecurityId")]
        public int Id { get; set; }

        public DateTime ReportDate { get; set; }
        public float? PriceClose { get; set; }
        public float? PriceOpen { get; set; }
        public float? PriceBase { get; set; }
        public float? PriceMax { get; set; }
        public float? PriceMin { get; set; }
        public double? Quantity { get; set; }
        public double? Monetary { get; set; }
        public int? TransactionsNumber { get; set; }
        public int? PriceType { get; set; }
        public DateTime? DateEnter { get; set; }
        public DateTime? DateUpdate { get; set; }
        public string Operator { get; set; }
    }

Thanks.

Filters "Property Path" doesn't work with nested List classes.

Filter "Property Path" doesn't work with nested List classes.

For example - you want to get Companies whose Employee id equals 5.
(Actual example would be with name, but this is simpler)
It is even be reproduced with your SampleWeb project.
You write the query

query{
  companies(where: {path:"Employees.Id"  comparison: equal value: "5" }){
    content
    employees{
      id
    }
  }
}

It does not work.

I would expect to get All companies which have employee whose id = 5.
I've tried to fiddle around with it, I'm pretty sure the problem is in the AggregatePath method
I tried just to make it work by a basic solution.

var strings = path.Split('.');
var firstProperty = Expression.PropertyOrField(parameter, strings.First());
var firstPropertiesItem = Expression.Property(firstProperty, "Item", Expression.Constant(0));
var actualProperty = Expression.Property(firstPropertiesItem, strings[1]);

It kind of takes the right property, but doesn't seem to work, as I'm not too familiar with ExpressionTrees.
I'm a bit new to github, so I didn't do any pull requests.

Clarify what the project does in README

There's already a similar project here: https://github.com/ckimes89/graphql-net (seems to be older than yours)

How would you compare it? Is it an alternative to the project above? Or it does something different?

Add EntityFramework Core IQueryable support to GraphQL

in readme doesn't tell much.

Also it would be helpful to have some kind of quickstart, how to get started without looking at the example project.

IQueryable Select statements

Hi Simon,

Does it only select the fields from IQueryable like Dapper ? Dapper seems alot of coding for each resolver so it would be nice not to have to implement Dapper.

'after' not excluding the indicated cursor value

It seems like the results should come after the "after" indicator; if I say "after 5" the first item should not be "5", it should be the on after that, since "5" would have been the endCursor on the previous page (and would thus appear on both pages)

image

Authorization

Where is the correct place to check if User has right Claims - otherwise return 401/some error message?
For example - I would like to get list of Employees only if User has claim called role: admin.
How to achieve this authorization stuff?
Thanks!

AddNavigationField on a many-to-many relationship (via a cross table)

I have 2 entities (Track and Album) that have a many-to-many relationship via a cross table (TrackXAlbum) which works great, but not as a navigation field.

AddNavigationField expects the lambda to resolve with the provided type of entity given as the second generic type parameter, which can only be resolved by using a .Select call on the IQuerable instance returned by the Where call.

Example on the AlbumGraph:

AddNavigationField<TrackGraph, Track>(
      "tracks",
      resolve: ctx => db.TrackXAlbums.Where(e => e.AlbumId == ctx.Source.Id).Select(e => e.Track)
);

Which causes the exception:
GraphQL.ExecutionError: Error trying to resolve albums. ---> System.NullReferenceException: Object reference not set to an instance of an object.

I ended up using the AddQueryField method instead of the AddNavigationField. Which does work, but with a very big performance impact. Probably due to the Select call.

How would I go about a many-to-many relationship using the AddNavigationField method, or do I need to use a different method?

Demo/Sample clarifications

Hi Simon

Sorry but why did you put 2 times employees inside CompanyGraph:

public class CompanyGraph : EfObjectGraphType<Company>
{
    public CompanyGraph(IEfGraphQLService graphQlService) : base(graphQlService)
    {
        Field(x => x.Id);
        Field(x => x.Content);
        AddNavigationField< EmployeeGraph, Employee>(
            name: "employees",
            resolve: context => context.Source.Employees);
        AddNavigationConnectionField<EmployeeGraph, Employee>(
            name: "employeesConnection",
            resolve: context => context.Source.Employees,
            includeNames: new []{ "Employees"});
    }
}

Error when writing In comparison on nullable type.

Describe the bug

TypeConverter fails to convert list of string values to list
of nullable types.

throw new Exception($"Could not convert strings to {type.FullName} ");

Line 46.

Minimal Repro

{
 testEntities
 (where: {
   path: "NullableProperty",
   comparison: "in",
   value: ["Value1", "Value2"]})
 {
   property
 }
}

Submit a PR that fixes the bug

TBD

Remove navigation overrides from root query

Since navigation properties dont exist on the root query, the following members are redundant

    FieldType AddNavigationField<TReturn>(ObjectGraphType graph,
        string name,
        Func<ResolveFieldContext<object>, TReturn> resolve,
        Type graphType = null,
        IEnumerable<QueryArgument> arguments = null,
        IEnumerable<string> includeNames = null)
        where TReturn : class;

    FieldType AddNavigationListField<TReturn>(ObjectGraphType graph,
        string name,
        Func<ResolveFieldContext<object>, IEnumerable<TReturn>> resolve,
        Type graphType = null,
        IEnumerable<QueryArgument> arguments = null,
        IEnumerable<string> includeNames = null)
        where TReturn : class;

    void AddNavigationConnectionField<TReturn>(
        ObjectGraphType graph,
        string name,
        Func<ResolveFieldContext<object>, IEnumerable<TReturn>> resolve,
        Type graphType = null,
        IEnumerable<QueryArgument> arguments = null,
        IEnumerable<string> includeNames = null,
        int pageSize = 10)
        where TReturn : class;

b54dd2b

Mutation not working

It throw expection
'Introspection must provide output type for fields.' on fields

Auto generate graphtypes

Hi

I just started looking at graphql, and started with the core library.
I quickly went on to https://github.com/graphql-dotnet/conventions, which was pretty nice to include all types without typing them out.

But, it's not very dynamic, and therefore trying this library, and I got it working very nicely.

However, the generation of the graphtypes like the conventions library does would be nice.

Has any work been done on that? Or am I missing something?

Any hints or hurdles I may bump into that you know about?

If I make a stab at it, is depending on the conventions library the wrong way to go?

running the repo

Hi,

Building works, trying to run the repo. I get

System.IO.FileLoadException: Could not load file or assembly 'System.Memory, Version=4.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The located assembly's manifest definition does not match the assembly reference
source: "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"
on hostBuilder.Build().Run(); in Program.cs

image

QueryPaginationField proposal

Hi,
i really like your implementation of query fields and query connection fields but i miss something in the middle - I need to paginate query field results, but there are no pageinfo meta for that purpose. I came from PHP (Laravel) enviroment and there pagination is implemented into GraphQL like this:

image

I imagine it somehow similar, query variables like this:
image

And inside that, it could be like this:

queryname {
	items: {
		.
		.
		.
	},
	totalCount
	perPage
	currentPage
	from
	to	
}

What do you think about this proposal, would you merge it if i make some PR?

Make ArgumentProcessor public or release as separate library.

Is the feature request related to a problem

For my project it would be useful to convert persisted qraphql queries to linq queries in order to implement subscriptions.

Describe the solution

Make ArgumentProcessor public.

Describe alternatives considered

Release separate library in order to convert GraphQl query to IQueryable.

Additional context

Maybe I'm doing something horrible, but i haven't found another way to convert IQueryable to IObservable which is necessary to return from EventStreamResolvers subscribe method.

Register multiple IModels via RegisterInContainer

I'm currently working on a POC to implement a single GraphQL service for multiple microservices. However, I can't seem to use more than one IModel via RegisterInContainer. Looking at the code (my complements on the high quality btw!), I see only the last one is actually used. Is it technically possible to allow multiple DbContexts to be used within the same service?

Or would you suggest to use one GraphQL endpoint per microservice?
Or create one additional DbContext that contains all entities in it's IModel and use that one instead?

Thanks :)

Autofac and EfGraphQLConventions.RegisterInContainer static method

Currently my code for GraphQL.EntityFramework registration looks as per the guide:

public void ConfigureServices(IServiceCollection services)
{
     services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
     using (var context = new MyDbContext())
     {
          EfGraphQLConventions.RegisterInContainer(services, context);
     }
     services.ConfigureNSwag();
}

The thing is I am also using Autofac and would like to be able to do this registration inside one of the Autofac modules, but I've been having no success.

How am I to go about doing this? Also, is there a way to do this registration without calling the static EfGraphQLConventions.RegisterInContainer method?

Can't run successfully

Code in startup.cs like this(modified according to sample):

var optionsBuilder = new DbContextOptionsBuilder<NHLStatsContext>(); optionsBuilder.UseMySql(Configuration["ConnectionStrings:Default"], b => b.MigrationsAssembly("NHLStats.Api")); services.AddDbContext<NHLStatsContext>(options => options.UseMySql(Configuration["ConnectionStrings:Default"], b => b.MigrationsAssembly("NHLStats.Api"))); EfGraphQLConventions.RegisterConnectionTypesInContainer(services); using (var myDataContext = new NHLStatsContext(optionsBuilder.Options)) { EfGraphQLConventions.RegisterInContainer(services, myDataContext); }

Errors:

image

Paging with Connections - Clarifications about cursors

GraphQL.EntityFramework implementation for paging with Connection model seems to use cursor sequence like "0", "1", "2", ...

Assuming a pagination with 100 items and paging with 10 items per page
I would like to implement feature like "Go directly to page 7".

Is it safe and reliable to assume I can query directly with arguments (first:10, after: X) where X is computed mathematically ?

I'm aware this is a workaround and only possible because of the cursor sequence. It would not work Facebook approach (documentation about Connection Model, show examples with base64 string for cursor : https://graphql.github.io/learn/pagination/)

Possible to return single result from `AddQueryField` for current user?

Premise

I'm trying to implement a root-level query field that returns a User for the currently logged-in user. For example, I'd like to be able to make the following GraphQL request:

{
  me {
    fullName
    profile {
      id
    }
  }
}

Issue

AddQueryField<UserGraph, User>(
    name: "me",
    resolve: context =>
    {
        var userContext = (GraphQLUserContext)context.UserContext;
        return userContext.dbContext.Users.Find(userContext.CurrentUser.Id);
    });

This throws the following error:

 "Cannot implicitly convert type 'CareSwitchCommon.Models.User' to 'System.Linq.IQueryable<CareSwitchCommon.Models.User>'. An explicit conversion exists (are you missing a cast?) [CareSwitchCommon]"

This suggests that AddQueryField only works with resolve functions that return an IQueryable that contains lists of entities.


Attempted Solution

At this point you may say that because this field is returning a single element I should use Field() instead of AddQueryField(). Fair enough, so I gave that a shot and was able to make more progress:

Field<UserGraph>(
    name: "me",
    resolve: context =>
    {
        var userContext = (GraphQLUserContext)context.UserContext;
        return userContext.CurrentUser
    });

The problem now was getting the profile navigation property on user to populate. This is what the relevant portions of UserGraph look like:

public UserGraph(IEfGraphQLService graphQLService) : base(graphQLService)
{
    Field(u => u.Id);
   
    AddNavigationField<ProfileGraph, Profile>(
        name: "profile",
        resolve: context =>
        {
            return context.Source.Profile;
        });
}

Specifically, it seems like context.Source.Profile does not populate when the source is a regular field and not a query field. I was able to further hack it by doing dbContext.Entry(context.Source).Reference(u => u.Profile).Query().Single(); but that was not ideal because it leads to the n+1 sql query issue and defeats the whole purpose of the intelligent Include() calls that GraphQL.EntityFramework is supposed to take care of.


Conclusion

It's not clear to me the best way to move forward to implement a me query that returns EF Core data associated with the User that's currently logged-in, and have it properly working with navigation properties.

Improve final DB query "size"

The current implementation of the "Query" fields in this library are potentially very heavy.

Each query that eventually gets built is the equivalent of a SELECT * FROM mytable;, instead of leveraging IQueryable and expression building to generate a "thin" select based on requested fields.

This is bad news when tables can contain very large fields (think news articles, or large file data during a "summary" query).

Solution?

Build the select expressions dynamically via expression building to handle only selecting required fields from the database.

This means that the Navigation query system will need to basically be "rewritten" to account for the total change from using .Include("Navigation") to something more akin to new { Navigation = new Navigation { ... } }.

Possibly this can be optional on a per-query basis?

We should be able to cache the select queries in some way based on the incoming query structure, so we only have a one-time build performance hit. (Though, larger query sets, could cause potential memory consumption issues?)

I have already written code locally to handle the base "select only this table" builder, when navigation properties are not used. This vastly speeds up DB access, and dramatically lowers bandwidth requirements at the same time.

Only question is, where/how would we approach the navigation property issue?

Contributing Back to GraphQL.NET

GraphQL.NET connections require you to write a lot of boilerplate code right now. I raised an issue called How Can We Simplify Creating Connections showing said boilerplate code and one potential solution to it.

When I came across your project, I thought this is another potential solition for those using EF Core or IQueryable in general as I don't think this project needs to be specific to EF Core. Have you considered raising a PR to contribute your AddQueryConnectionField code in particular back to the GraphQL.NET project?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.