Graphql-Mutation : Schema Exception - graphql

I am trying to implement GraphQL -mutation using HotChocolate. But there is some issue with the schema (https://localhost:1234/graphql?sdl) and I am getting the unhandled exception:
The schema builder was unable to identify the query type of the schema. Either specify which type is the query type or set the schema builder to non-strict validation mode.
SchemaException: For more details look at the Errors property. 1. The schema builder was unable to identify the query type of the schema. Either specify which type is the query type or set the schema builder to non-strict validation mode.
My mutation code:
using HotChocolate
public class Book
{
public int Id { get; set; }
[GraphQLNonNullType]
public string Title { get; set; }
public int Pages { get; set; }
public int Chapters { get; set; }
}
public class Mutation
{
public async Task<Book> Book(string title, int pages, string author, int chapters)
{
var book = new Book
{
Title = title,
Chapters = chapters,
Pages = pages,
};
return book;
}
}
I have added the following in the API startup.cs file
public void ConfigureServices(IServiceCollection services)
{
services.AddGraphQLServer().AddMutationType<Mutation>();
}

You can try this:
public void ConfigureServices(IServiceCollection services)
{
services.AddGraphQLServer()
.ConfigureSchema(sb => sb.ModifyOptions(opts => opts.StrictValidation = false))
.AddMutationType<Mutation>();
}

This seems to happen when you register only a mutation type. The error goes away if you also register a query type.
So you could add the following to your code
public class Query
{
public string HelloWorld()
{
return "Hello, from GraphQL!";
}
}
And register it in your services:
public void ConfigureServices(IServiceCollection services)
{
services.AddGraphQLServer()
.AddMutationType<Mutation>()
.AddQueryType<Query>();
}
Obviously this isnt an ideal solution, as it's possible that you intend fr your API to only have mutations (although that's probably unlikely). If, like me, your API will support both queries and mutations but you just started building the mutations, you could add this query in as a place holder for now.

Related

swagger swashbuckle does not support nested class as action method parameter

I am using asp.net 5
I have two model class, which are nested, both of the inner class are named Command
public class EditModel
{
public class Command
{
public int Id { get; set; }
public string Info { get; set; }
}
}
and
public class CreateModel
{
public class Command
{
public int Id { get; set; }
public string Info { get; set; }
}
}
In my Controller class has two methods
[HttpPost]
public IActionResult PutData(CreateModel.Command model)
{
return Ok();
}
[HttpPut]
public IActionResult PostData(EditModel.Command model)
{
return Ok();
}
Since for both Put and Post's query I am using nested class both name Command, Swagger will return the following error
An unhandled exception has occurred while executing the request.
Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Conflicting method/path combination "PUT Test" for actions -
TestSwagger.Controllers.TestController.PutData
(TestSwagger),TestSwagger.Controllers.TestController.PostData
(TestSwagger). Actions require a unique method/path combination for
Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable1 apiDescriptions, SchemaRepository schemaRepository) at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable1
apiDescriptions, SchemaRepository schemaRepository)
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String
documentName, String host, String basePath)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext
httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)
Swagger will work, if I change one of the Command model name to something different.
Yet, I believe this nested class model name is legit and should work with swagger also. If there a way to work around this. Thanks
By adding c.CustomSchemaIds(x => x.FullName);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestSwagger", Version = "v1" });
c.CustomSchemaIds(x => x.FullName);
});
solved the schemaId conflict. Thanks to this question

Web api Data Annotations cause 500 server error when passed

I am creating a web api project and want to add validation to my model, so I added a DataAnnotation attribute.
I then tested the project by trying to pass my object from a separate mvc project. I recieved a 500 server error.
Removing the DataAnnotation allows me to pass the object successfully. Why?
I have looked at a couple of tutorials such as this and this, they show how to handle validation errors, but this has not helped.
UPDATE
Removing the data annotation from MyProperty in Class1, solution B (but leaving it on the class in solution A) means values can be passed successfully! Is this a problem with deserilazing the object? If so how do I solve it?
B = My web service reciving the object
A = My mvc project sending the object
my code to send the resquest
public class Class1
{
[Required(ErrorMessage = "MyProperty value is required")]//remove this line to make it work
public int MyProperty { get; set; }
public Class2 MyOtherProperty { get; set; }
}
public class Class2
{
public string SomeProperty { get; set; }
}
async Task<string> Test2()
{
var form = new Class1();
form.MyProperty = 123;
form.Class2 = new Class2();
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:58814/api/");
var post = await client.PostAsJsonAsync<Class1>("Values", form);
var putt = await client.PutAsJsonAsync<Class1>("Values", form);
}
return "";
}
my code to recieve the request (the breakpoint applied is not being hit)
// POST api/values
public void Post([FromBody]Class1 value)
{
}
// PUT api/values/5
public void Put([FromBody]Class1 value)
{
}
I have resolve the issue thanks to the advice from this link - Scott Hanselman's blog
My sending project and receiving project had identical classes (including data annotations). Removing the annotations from the class in the receiving project resolved the binding issue but I still needed validation on the properties. Also changing the annotation from required to Range also resolved the binding issue (I only mention these as they were some steps I took to try and debug).
The final solution for me was to change this
public class Network
{
[Required(ErrorMessage = "NetworkID is required")]
public int NetworkID { get; set; }
}
to this
[DataContract]
public class Network
{
[DataMember(IsRequired = true)]
public int NetworkID { get; set; }
}

MVC3 - Unity/Unit of Work Pattern and Webservice implementation

I am a newbie to with unity and unit of work pattern and I am trying to write a code, which connects to my webservice and does all the work.
Everything goes well until I use the Database but I get lost when I try to use the webservice.
I have wasted my 2 precious days, searching every single possible article related to it and applying it to my code, but no luck till date.
I know, by writing connection string to web.config and calling it in dbcontext class controller will connect to the required database, but I am not connecting to any database, so what changes I need to do in web/app.config. Also, even if I write my connection logic in dbcontext constructor, it still searches and fills the dbcontext with sql server details. I presume thats happening because I am using DBSet.
Guys, you are requested to have a look at my code, I have done and show me some hope that I can do it. Let me know, if you want any other info related to the code that you want to see.
thanks
DBCONTEXT
public class CVSContext : DbContext
{
public DbSet<CVSViewModel> CVS { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<Account> Accounts { get; set; }
public CVSContext()
{
//CRM Start
var clientCredentials = new System.ServiceModel.Description.ClientCredentials();
clientCredentials.UserName.UserName = "";
clientCredentials.UserName.Password = "";
var serviceProxy = new Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy(new Uri("http://Organization.svc"), null, clientCredentials, null);
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
HttpContext.Current.Session.Add("ServiceProxy", serviceProxy);
//CRM End
}
}
GENERIC REPOSITORY
public class GenericRepository<TEntity> where TEntity : class
{
internal CVSContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(CVSContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
}
UNIT OF WORK
public interface IUnitOfWork : IDisposable
{
int SaveChanges();
}
public interface IDALContext : IUnitOfWork
{
ICVSRepository CVS { get; set; }
IContactRepository Contacts { get; set; }
//IAccountRepository Accounts { get; set; }
}
public class DALContext : IDALContext
{
private CVSContext dbContext;
private ICVSRepository cvs;
private IContactRepository contacts;
// private IAccountRepository accounts;
public DALContext()
{
dbContext = new CVSContext();
}
public ICVSRepository CVS
{
get
{
if (cvs == null)
cvs = new CVSRepository(dbContext);
return cvs;
}
set
{
if (cvs == value)
cvs = value;
}
}
public IContactRepository Contacts
{
get
{
if (contacts == null)
contacts = new ContactRepository(dbContext);
return contacts;
}
set
{
if (contacts == value)
contacts = value;
}
}
public int SaveChanges()
{
return this.SaveChanges();
}
public void Dispose()
{
if(contacts != null)
contacts.Dispose();
//if(accounts != null)
// accounts.Dispose();
if(dbContext != null)
dbContext.Dispose();
GC.SuppressFinalize(this);
}
}
SERVICE
public interface ICVSService
{
Contact CreateContact(Guid contactName, string productName, int price);
List<CVSViewModel> GetCVS();
List<Contact> GetContacts();
List<Account> GetAccounts();
}
public class CVSService : ICVSService, IDisposable
{
private IDALContext context;
public CVSService(IDALContext dal)
{
context = dal;
}
public List<CVSViewModel> GetCVS()
{
return context.CVS.All().ToList();
}
public List<Contact> GetContacts()
{
return context.Contacts.All().ToList();
}
public List<Account> GetAccounts()
{
return context.Accounts.All().ToList();
}
public Contact CreateContact(Guid contactName, string accountName, int price)
{
var contact = new Contact() { ContactId = contactName };
var account = new Account() { ContactName = accountName, Rent = price, Contact = contact };
//context.Contacts.Create(contact);
context.SaveChanges();
return contact;
}
public void Dispose()
{
if (context != null)
context.Dispose();
}
}
CONTROLLER
public ActionResult Index()
{
ViewData.Model = service.GetContacts();
return View();
}
It's all about proper abstractions. The common abstraction that is used between some data source (could be a db or ws) is the Repository pattern, or at a higher level the Unit of Work pattern. In fact Entity Framework DbContext is an implementation of the Unit of Work pattern, but it is tailored for databases. You can't use to communicate with a web service.
In that case you will have to write your own IRepository<T> abstraction and have a database specific implementation that uses a DbContext under the covers and a web service specific implementation that wraps a web service client proxy under the covers.
However, when your application gets more complex, you often find yourself wanting to have some sort of transaction like behavior. This is what the Unit of Work pattern if for: it presents a business transaction. Using the unit of work pattern to wrap multiple WS calls however, will get painful very soon. It's a lot of work to get right and in that case you will be much better of using a message based architecture.
With a message based architecture you define a single atomic operation (a business transaction or use case) as a specific message, for instance:
public class MoveCustomerCommand
{
public int CustomerId { get; set; }
public Address NewAddress { get; set; }
}
This is just an object (DTO) with a set of properties, but without behavior. Nice about this is that you can pass these kinds of objects over the wire using WCF or any other technology or process them locally without the need for the consumer to know.
Take a look at this article that describes it in detail. This article builds on top of that model and describes how you can write highly maintainable WCF services using this model.

How to convert ObjectSet from Entities to IEnumerable of Models?

I am building a test application using MVC3, Razor, and Entity Framework 4.1 with a schema-first approach (as apposed to a code-first approach), in a repository pattern. I would like to avoid accessing data objects in my view, and access a model instead, but I am having a problem. As far as I can tell, the data objects are being returned from the data layer as ObjectSet, but my View needs IEnumerable, and I don't know how to cast one to the other.
Here is some code, to help clarify.
Model ...
namespace TestSolution.Models
{
public class ProjectModel
{
[HiddenInput]
public int Id { get; set; }
[Required]
[StringLength(255, ErrorMessage = "The name cannot be more than 255 characters long.")]
[Display(Name = "Name")]
public string Name { get; set; }
[Required]
[Display(Name = "Description")]
public string Description { get; set; }
}
}
Repository ...
public IQueryable<ProjectModel> GetProjects()
{
return Db.Project;
}
Entities ...
public ObjectSet<Project> Project
{
get
{
if ((_Project == null))
{
_Project = base.CreateObjectSet<Project>("Project");
}
return _Project;
}
}
Controller ...
public ActionResult Index()
{
IEnumerable<TestSolution.Models.ProjectModel> model = _projectRepository.GetProjects();
return View(model);
}
View ...
#model IEnumerable<TestSolution.Models.ProjectModel>
Error I am getting when building ...
Cannot implicitly convert type 'System.Data.Objects.ObjectSet<TestSolution.Project>' to 'System.Linq.IQueryable<TestSolution.Models.ProjectModel>'. An explicit conversion exists (are you missing a cast?)
Does this question make sense? I am just not sure where go from here ... any advise you guys can give me would be awesome. :)
EDIT: I was able to solve this with Kyle's suggestion by changing my Repository code to ...
public IQueryable<ProjectModel> GetProjects()
{
return Db.Project.Select(i => new ProjectModel() { Id = i.Id, Name = i.Name, Description = i.Description });
}
The problem isn't converting from ObjectSet<T> to IEnumerable<T> (ObjectSet<T> implements IEnumerable<T>).
The problem is converting from TestSolution.Project to TestSolution.Models.ProjectModel. You will need to write some conversion code, maybe something similar to the below:
model.Select(i => new ProjectModel() { /* Set properties here. */ });

OrderBy using RavenDB-an exception

I use Raven Db in my project. Im trying to order list like below:
list.OrderByDescending(x => x.Supporters.Sum(y=>y.Tokens));
And i got an exception like this:
{"Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'."}
Model looks as below:
public class Idea
{
(...)
public IList<IdeaSupporter> Supporters { get; set; }
}
public class IdeaSupporter
{
(...)
public int Tokens { get; set; }
}
What do i wrong?
Thanks for any help.
You cannot sort on a computation, but you can define an index like this:
from idea in docs.Ideas
select new
{
SumOfSupportersTokens = idea.Supporters.Sum(x=>x.Tokens)
}
And then sort on SumOfSupportersTokens

Resources