My domain entity aggregate root looks like this:
public class Book : Entity<long>
{
private BookInformation _bookInformation;
private bool _inStock;
public BookInformation BookInformation => _bookInformation;
public bool InStock => _inStock;
public Book() { }
private Book(BookInformation bookInformation)
{
_bookInformation = bookInformation;
_inStock = true;
}
public static Book Create(string title, string author, string subject, string isbn)
{
var bookInformation = new BookInformation(title, author, subject, isbn);
var book = new Book(bookInformation);
return book;
}
// and other methods (...)
}
And my Entity class has a list of events (of type INotification):
private List<INotification> _domainEvents;
public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();
INotification is a markup interface from the MediatR library. My problem is the following - when I add GraphQL with the following setup:
services.AddGraphQL(
SchemaBuilder.New()
.AddQueryType<QueryType>()
.Create(),
new QueryExecutionOptions
{
ForceSerialExecution = true
});
(...)
app.UseGraphQL().UsePlayground();
and the Query:
public class Query
{
[UseSelection]
public IQueryable<Book> GetBooks([Service] LibraryDbContext context)
=> context.Books;
}
I get the following error on project startup
An unhandled exception of type 'HotChocolate.SchemaException' occurred in
HotChocolate.Types.dll: 'InterfaceINotificationhas no fields declared. - Type: INotification'
Stack trace:
at HotChocolate.Configuration.TypeInitializer.EnsureNoErrors()
at HotChocolate.Configuration.TypeInitializer.CompleteTypes(DiscoveredTypes discoveredTypes)
at HotChocolate.Configuration.TypeInitializer.Initialize(Func`1 schemaResolver, IReadOnlySchemaOptions options)
at HotChocolate.SchemaBuilder.Create()
at HotChocolate.SchemaBuilder.HotChocolate.ISchemaBuilder.Create()
I tried to introduce QueryType like this:
public class QueryType : ObjectType<Query>
{
protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
// How to ignore these DomainEvents property here from the Query?
}
}
but I don't know how can I ignore the DomainEvents property from the model. How can I achieve this? Or am I doing this from the wrong side?
You can add
[GraphQLIgnore]
public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();
or create an object type for book:
public class QueryType : ObjectType<Book>
{
protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
{
descriptor.Ignore(x => x.DomainEvents);
}
}
if there are actually different type of events you can also add Interfaces Or Union Types based on the interface
Indeed, adding [GraphQLIgnore] attribute works well. But as far as I understand if I create this QueryType for Book I have to use it when register graphql in my IServiceCollection by .AddQueryType<QueryType>() am I right?
But then my graphql playground shows me this:

there is no Books array, why?
And this:

is not returning any correct value
ah i just copied the class from your example.
so:
public class BookType : ObjectType<Book>
{
protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
{
descriptor.Ignore(x => x.DomainEvents);
}
}
`
But you still need the root type
public class QueryType : ObjectType<Query>
{
protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
}
}
`
you can also make this with pure code first with this on the Books class
[GraphQLIgnore]
public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();
adn this on the schema builder:
.AddQueryType<Query>()
@PascalSenn thanks for the answer :)
If I have only .AddQueryType<Query>() registered it still says that Interface INotification has no fields declared. But if I add .AddQueryType<BookType>() the error is following:
System.ArgumentException: 'An item with the same key has already been added. Key: Query'
Here is my registration:
services.AddGraphQL(
SchemaBuilder.New()
.AddQueryType<Query>()
.AddQueryType<BookType>()
.Create(),
new QueryExecutionOptions
{
ForceSerialExecution = true
});
and here are my query types:
public class BookType : ObjectType<Book>
{
protected override void Configure(IObjectTypeDescriptor<Book> descriptor)
{
descriptor.Ignore(x => x.DomainEvents);
}
}
public class QueryType : ObjectType<Query>
{
protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
}
}
public class Query
{
[UseSelection]
public IQueryable<Book> GetBooks([Service] LibraryDbContext context)
=> context.Books;
}
Yes you only need to add one query type
SchemaBuilder.New()
.AddQueryType<Query>()
.AddType<BookType>()
.Create(),
you also do not need QueryType in this case 👍
@PascalSenn Great, it works! :)
I have another question. Do I need a public ctor in my Book entity? I had private parameterless ctor because EFCore needs it, but I see that GraphQL needs a public one. Is there any way to keep my entity encapsulated like before (private ctor). The same question about missing 'set accesor on my properties:
The property 'System.String Author' has no 'set' accessor (Parameter 'member')"
Can I somehow workaround this?
I have a public property: public bool InStock => _inStock; and it reads a value from a backend field private bool _inStock;
Unless you use it as an input type you do not need any ctor
in case of an input type we map all public {get,set,} properties into the schema.
you can also have readonly {get;} properties that are set over a pulbic ctor
public class FooInput {
public string Bar {get;set;}
}
public class FooInput {
public FooInput(string bar)
{
Bar = bar;
}
public string Bar {get;}
}
So it is not able to with with backend fields like in my case?
public class Book : Entity<long>
{
private bool _inStock;
public bool InStock => _inStock; // This is a property I would like to query with GraphQL
private BookInformation _bookInformation;
public BookInformation BookInformation => _bookInformation;
private Book(BookInformation bookInformation)
{
_bookInformation = bookInformation;
_inStock = true;
}
public static Book Create(string title, string author, string subject, string isbn)
{
var bookInformation = new BookInformation(title, author, subject, isbn);
var book = new Book(bookInformation);
return book;
}
}
(...)
This should work
This should work
But I have this error:

Ah, yes with UseSelection it does not work. sorry for the inconvenience. You can not write this as a type safe select statement:
bboks,Select(x => new Book(){InStock = x.InStock});
Indeed, without UseSelection attribute it works but of course the generated SQL query is very inefficient :(
Ok i am gonna close this issue as the original question is answered 👍