I'm trying to implement a GraphQL API that in .NET using Hot Chocolate 12.3.2. I'm able to get the project to build and run. When attempting to test my query CakePop the browser loads fine but there is no schema present.
startup.cs
namespace Application
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
[Obsolete]
public void ConfigureServices(IServiceCollection services)
{
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddType<BillType>().AddGraphQL()
.BindRuntimeType<DateOnly, DateType>()
.AddTypeConverter<DateOnly, DateTime>(from => DateTime.Parse(from.ToString()))
.AddTypeConverter<DateTime, DateOnly>(from => DateOnly.FromDateTime(from));
services.AddGraphQL(sp => SchemaBuilder.New().AddServices(sp).Create())
.AddPooledDbContextFactory<WeVoteContext>(b =>b.UseInMemoryDatabase("Test"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app
.UseRouting()
.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL().WithOptions(new GraphQLServerOptions
{
EnableSchemaRequests = true,
Tool = {
Enable = env.IsDevelopment()
}
});
});
}
}
}
query.cs
namespace Application
{
public class Query
{
static ApplicationContext db = new ApplicationContext();
public IEnumerable<LegiscanModelBill> GetBills(int N)
{
return db.LegiscanModelBills.Take(N).AsEnumerable();
}
}
}
Models/LegiscanModelBill.cs
namespace Application.Models
{
public partial class LegiscanModelBill
{
[GraphQLIgnore]
public int Id { get; set; }
public string? BillNumber { get; set; }
public string? ChangeHash { get; set; }
public string? Url { get; set; }
[GraphQLIgnore]
public DateOnly? StatusDate { get; set; }
public int? StatusId { get; set; }
[GraphQLIgnore]
public DateOnly? LastActionDate { get; set; }
public string? LastAction { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
[GraphQLIgnore]
public DateTime CreatedAt { get; set; }
[GraphQLIgnore]
public DateTime UpdatedAt { get; set; }
public int? SessionId { get; set; }
public int? UpvotesCount { get; set; }
public int? BodyId { get; set; }
public DateTime? LegiscanDataUpdatedAt { get; set; }
public int? BillId { get; set; }
public int? CommentsCount { get; set; }
public double? Hotness { get; set; }
public string? Texts { get; set; }
public int? CommitteeId { get; set; }
public int? BillTypeId { get; set; }
public string? StateLink { get; set; }
}
public class BillType : ObjectType<LegiscanModelBill>
{
}
}
Nothing is in the logs
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
Updated Service
public void ConfigureServices(IServiceCollection services)
{
services
.AddPooledDbContextFactory<WeVoteContext>(b => b.UseInMemoryDatabase("Test"))
.AddGraphQLServer()
.AddQueryType<Query>()
.AddType<BillType>();
// .BindRuntimeType<DateOnly, DateType>()
// .AddTypeConverter<DateOnly, DateTime>(from => from.ToDateTime(default))
// .AddTypeConverter<DateTime, DateOnly>(from => DateOnly.FromDateTime(from.Date));
}
The setup code seems to have an issue ... you are mixing legacy API and the new configuration API.
services
.AddPooledDbContextFactory<WeVoteContext>(b =>b.UseInMemoryDatabase("Test"))
.AddGraphQLServer()
.AddQueryType<Query>()
.AddType<BillType>()
.BindRuntimeType<DateOnly, DateType>()
.AddTypeConverter<DateOnly, DateTime>(from => from.ToDateTime(default))
.AddTypeConverter<DateTime, DateOnly>(from => DateOnly.FromDateTime(from.Date));
With 12.4.0-preview.8 we have shipped support for TimeOnly and DateOnly.
When you move to 12.4 remove the explicit binding and the converters.
Also, you can remove the GraphQLIgnore attributes with this configuration again.
Related
I have an Asp.Net web API project. And I'm implementing a shopping cart. So I have models that have to be converted to Dtos. So I'm using automapper to map Models to Dtos so I can transfer data but for some reason, I'm getting the following error.
The error message I get:
"Error mapping types.\r\n\r\nMapping types:\r\nCartModel -> CartDto\r\nMoby.Services.ShoppingCart.API.Models.CartModel -> Moby.Services.ShoppingCart.API.Models.Dto.CartDto\r\n\r\nType Map configuration:\r\nCartModel -> CartDto\r\nMoby.Services.ShoppingCart.API.Models.CartModel -> Moby.Services.ShoppingCart.API.Models.Dto.CartDto\r\n\r\nDestination Member:\r\nCartDetails\r\n"
Automapper config class:
public class MapperConfig
{
public static MapperConfiguration RegisterMaps()
{
return new MapperConfiguration(config =>
{
config.CreateMap<CartModel, CartDto>().ReverseMap();
config.CreateMap<CartHeaderModel, CartHeaderDto>().ReverseMap();
config.CreateMap<CartDetailsModel, CartDetailsDto>().ReverseMap();
config.CreateMap<ProductModel, ProductDto>().ReverseMap();
});
}
}
Automapper dependency injection:
var mapper = MapperConfig.RegisterMaps().CreateMapper();
builder.Services.AddSingleton(mapper);
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
CartModel:
public class CartModel
{
public CartHeaderModel CartHeader { get; set; }
public IEnumerable<CartDetailsModel> CartDetails { get; set; }
}
CartDto:
public class CartDto
{
public CartHeaderDto CartHeader { get; set; }
public IEnumerable<CartDetailsDto> CartDetails { get; set; }
}
CartDetailsModel:
public int Id { get; set; }
public int CartHeaderId { get; set; }
[ForeignKey(nameof(CartHeaderId))]
public virtual CartHeaderModel CartHeader { get; set; }
public int ProductId { get; set; }
[ForeignKey(nameof(ProductId))]
public virtual ProductModel Product { get; set; }
public int Count { get; set; }
CartDetailsDto
public class CartDetailsDto
{
public int Id { get; set; }
public int CartHeaderId { get; set; }
public virtual CartHeaderDto CartHeader { get; set; }
public int ProductId { get; set; }
public virtual ProductModel Product { get; set; }
public int Count { get; set; }
}
Define your mapping inside a class that extends Profile class.
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<CartModel, CartDto>().ReverseMap();
CreateMap<CartHeaderModel, CartHeaderDto>().ReverseMap();
CreateMap<CartDetailsModel, CartDetailsDto>().ReverseMap();
CreateMap<ProductModel, ProductDto>().ReverseMap();
}
}
Remove these lines:
var mapper = MapperConfig.RegisterMaps().CreateMapper();
builder.Services.AddSingleton(mapper);
Only keep this line:
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
AddAutoMapper will scan the assemblies for any classes that extend Profile and load the mapping configuration from them. I will also register a mapper that you can inject in your services (IMapper).
In my web API when I run project to get data from the database got this error
.net core 3.1
JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.
These are my codes:
my Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
my productCategory class is:
public class ProductCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string CatText { get; set; }
public string ImagePath { get; set; }
public int Priority { get; set; }
public int Viewd { get; set; }
public string Description { get; set; }
public bool Active { get; set; }
public DateTime CreateDate { get; set; }
public DateTime ModifyDate { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
my repo is
public async Task<IList<Product>> GetAllProductAsync()
{
return await _context.Products.Include(p => p.ProductCategory).ToListAsync();
}
my interface
public interface IProductRepository
{
...
Task<IList<Product>> GetAllProductAsync();
...
}
and this is my controller in api project
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public ActionResult Get()
{
return Ok(_productRepository.GetAllProduct());
}
}
When I run API project and put this URL: https://localhost:44397/api/products
I got that error,
I can't resolve it
this is happening because your data have a reference loop.
e.g
// this example creates a reference loop
var p = new Product()
{
ProductCategory = new ProductCategory()
{ products = new List<Product>() }
};
p.ProductCategory.products.Add(p); // <- this create the loop
var x = JsonSerializer.Serialize(p); // A possible object cycle was detected ...
You can not handle the reference loop situation in the new System.Text.Json yet (netcore 3.1.1) unless you completely ignore a reference and its not a good idea always. (using [JsonIgnore] attribute)
but you have two options to fix this.
you can use Newtonsoft.Json in your project instead of System.Text.Json (i linked an article for you)
Download the System.Text.Json preview package version 5.0.0-alpha.1.20071.1 from dotnet5 gallery (through Visual Studio's NuGet client):
option 1 usage:
services.AddMvc()
.AddNewtonsoftJson(
options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
// if you not using .AddMvc use these methods instead
//services.AddControllers().AddNewtonsoftJson(...);
//services.AddControllersWithViews().AddNewtonsoftJson(...);
//services.AddRazorPages().AddNewtonsoftJson(...);
option 2 usage:
// for manual serializer
var options = new JsonSerializerOptions
{
ReferenceHandling = ReferenceHandling.Preserve
};
string json = JsonSerializer.Serialize(objectWithLoops, options);
// -----------------------------------------
// for asp.net core 3.1 (globaly)
services.AddMvc()
.AddJsonOptions(o => {
o.JsonSerializerOptions
.ReferenceHandling = ReferenceHandling.Preserve
});
these serializers have ReferenceLoopHandling feature.
Edit : ReferenceHandling changed to ReferenceHandler in DotNet 5
but if you decide to just ignore one reference use [JsonIgnore] on one of these properties. but it causes null result on your API response for that field even when you don't have a reference loop.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
public int ProductCategoryId { get; set; }
// [JsonIgnore] HERE or
public virtual ProductCategory ProductCategory { get; set; }
}
public class ProductCategory
{
public int Id { get; set; }
// [JsonIgnore] or HERE
public ICollection<Product> products {get;set;}
}
.NET 5 Web API
public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
}
I have the same issue, my fix was to add async and await keyword since I am calling an async method on my business logic.
Here is my original code:
[HttpGet]
public IActionResult Get()
{
//This is async method and I am not using await and async feature .NET which triggers the error
var results = _repository.GetAllDataAsync();
return Ok(results);
}
To this one:
HttpGet]
public async Task<IActionResult> Get()
{
var results = await _repository.GetAllDataAsync();
return Ok(results);
}
In .Net 6, you can use System.Text.Json to initialize a startup action with AddControllersWithViews like this in Program.cs,
using System.Text.Json.Serialization;
builder.Services.AddControllersWithViews()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
also you can use AddMvc like this,
builder.Services.AddMvc()
.AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
but quote from Ryan
asp.net core 3.0+ template use these new
methodsAddControllersWithViews,AddRazorPages,AddControllers instead of
AddMvc.
I will recommend to use the first solution.
Ensure you have [JsonIgnore] on the correct fields to avoid a circular reference.
In this case you will need
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string ProductText { get; set; }
[JsonIgnore]
public virtual ProductCategory ProductCategory { get; set; }
}
You probably don't need the ProductCategoryId field (depends if you are using EF and code first to define your DB)
Edit - In answer to noruk
There is often confusion in connected objects and navigation properties. You can get the data you want in JSON but also define the EF structures to get the correct DB structure (foreign keys, indexes, etc).
Take this simple example. A Product (for example a T-Shirt) has many sizes or SKUs (e.g. Small, Large, etc)
public class Product
{
[Key]
[MaxLength(50)]
public string Style { get; set; }
[MaxLength(255)]
public string Description { get; set; }
public List<Sku> Skus { get; set; }
}
public class Sku
{
[Key]
[MaxLength(50)]
public string Sku { get; set; }
[MaxLength(50)]
public string Barcode { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
// One to Many for Product
[JsonIgnore]
public Product Product { get; set; }
}
Here you can serialise a Product and the JSON data will include the SKUs. This is the normal way of doing things.
However if you serialise a SKU you will NOT get it's parent product. Including the navigation property will send you into the dreaded loop and throw the "object cycle was detected" error.
I know this is limiting in some use cases but I would suggest you follow this pattern and if you want the parent object available you fetch it separately based on the child.
var parent = dbContext.SKUs.Include(p => p.Product).First(s => s.Sku == "MY SKU").Product
I fixed my API Core Net6.0 adding [JsonIgnore]:
public class SubCategoryDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public int CategoryId { get; set; }
[JsonIgnore]
public Category Category { get; set; }
}
For net core 3.1 you have to add in Startup.cs:
services.AddMvc.AddJsonOptions(o => {
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
o.JsonSerializerOptions.MaxDepth = 0;
})
and import at least this package using nuget.org include prerelease:
<PackageReference Include="System.Text.Json" Version="5.0.0-rc.1.20451.14" />
following code is working for me in dotnet 5.0 :
services.AddControllersWithViews()
.AddJsonOptions(o => o.JsonSerializerOptions
.ReferenceHandler = ReferenceHandler.Preserve);
Finally fixed mine with System.Text.Json not NewtonSoft.Json using
var options = new JsonSerializerOptions()
{
MaxDepth = 0,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true
};
Using options to serialize
objstr = JsonSerializer.Serialize(obj,options);
My project built with a similar error.
Here's the code before
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
public PrimaryClass primaryClass { get; set; }
}
I took away the PrimaryClass object from the DependentClass model.
Code after
public class PrimaryClass {
public int PrimaryClassId
public ICollection<DependentClass> DependentClasses { get; set; }
}
public class DependentClass {
public int DependentClassId { get; set; }
public int PrimaryClassId { get; set; }
}
I also had to adjust the OnModelCreating method from
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses).WithOne(d => d.primaryClass).HasForeignKey(d => d.PrimaryClassId);
to
modelBuilder.Entity<PrimaryClass>().HasMany(p => p.DependentClasses);
The DbSet query that's running is
public async Task<List<DependentClass>> GetPrimaryClassDependentClasses(PrimaryClass p)
{
return await _dbContext.DependentClass.Where(dep => dep.PrimaryClassId == p.PrimaryClassId).ToListAsync();
}
The error could have been with any of these 3 sections of code, but removing the primary object reference from the dependent class and adjusting the OnModelCreating resolved the error, I'm just not sure why that would cause a cycle.
In my case the problem was when creating the entity relationships. I linked the main entity using a foreign key inside the dependent entity like this
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
also I referred the dipendend entity inside the main entity as well.
public List<Device> devices { get; set; }
which created a cycle.
Dependent Entity
public class Device
{
[Key]
public int id { get; set; }
public int asset_number { get; set; }
public string brand { get; set; }
public string model_name { get; set; }
public string model_no { get; set; }
public string serial_no { get; set; }
public string os { get; set; }
public string os_version { get; set; }
public string note { get; set; }
public bool shared { get; set; }
public int week_limit { get; set; }
public bool auto_acceptance { get; set; }
public bool booking_availability { get; set; }
public bool hide_device { get; set; }
public bool last_booked_id { get; set; }
//getting the relationships category 1 to many
public int category_id { get; set; }
[ForeignKey("category_id")]
public Device_Category Device_Category { get; set; }
public List<Booking> Bookings { get; set; }
}
Main Entity
public class Device_Category
{
public int id { get; set; }
public string name { get; set; }
public List<Device> devices { get; set; }
}
}
So I commented the
public List<Device> devices { get; set; }
inside main entity (Device_Category) and problem solved
I have the following classes:
public class Quiz
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string UserToken { get; set; }
public List<JoinQuizAndArea> AreasOfQuizzes { get; set; }
public List<QuizQuestion> Questions { get; set; }
}
public class QuizQuestion
{
public int ListRanking { get; set; }
public string Question { get; set; }
public string Answer1 { get; set; }
public string Answer2 { get; set; }
public string Answer3 { get; set; }
public int CorrectAnswer { get; set; }
public int QuizId { get; set; }
public Quiz Quiz { get; set; }
}
public class AreaOfQuiz
{
public int Id { get; set; }
public string Area { get; set; }
public List<JoinQuizAndArea> AreasOfQuizzes { get; set; }
}
public class JoinQuizAndArea
{
public int QuizId { get; set; }
public Quiz Quiz { get; set; }
public int AreaId { get; set; }
public AreaOfQuiz Area { get; set; }
}
and my DbContext:
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public DbSet<IdentityUser> Users { get; set; }
public DbSet<Quiz> Quizzes { get; set; }
public DbSet<AreaOfQuiz> Areas { get; set; }
public DbSet<QuizQuestion> Questions { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>();
modelBuilder.Entity<Quiz>();
modelBuilder.Entity<AreaOfQuiz>();
modelBuilder.Entity<QuizQuestion>()
.HasKey(quizQuestion => new { quizQuestion.QuizId, quizQuestion.ListRanking});
modelBuilder.Entity<JoinQuizAndArea>()
.HasKey(joinEntity => new { joinEntity.QuizId, joinEntity.AreaId });
modelBuilder.Entity<JoinQuizAndArea>()
.HasOne(join => join.Area)
.WithMany(c =>c.AreasOfQuizzes)
.HasForeignKey(join => join.QuizId);
modelBuilder.Entity<JoinQuizAndArea>()
.HasOne(bc => bc.Quiz)
.WithMany(c => c.AreasOfQuizzes)
.HasForeignKey(bc => bc.AreaId);
base.OnModelCreating(modelBuilder);
}
}
when I try to create the initial migration I got the following error:
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Custom configuration for members is only supported for top-level individual members on a type.
Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
Do someone have an idea how to resolve this problem?? Thanks :)
Update
My start Up class:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
var mappingConfig = new MapperConfiguration(mapConfig =>
{
mapConfig.AddProfile(new QuizProfile());
mapConfig.AddProfile(new AreaOfQuizProfile());
mapConfig.AddProfile(new QuizQuestionProfile());
mapConfig.AddProfile(new QuizIdAreaIdToJoinQuizAndAreaProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration["ConnectionString:QuizWorldDb"]));
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(config =>{
config.RequireHttpsMetadata=false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Secret"])),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
For me, unload docker-compose worked.
Clean and Build.
Run Migration.
This is the error I get:
System.InvalidOperationException: Unable to resolve service for type
'myBackEnd.Entities.Striper' while attempting to activate
'myBackEnd.Controllers.StripeController'.
at
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
I am working on using Automapper for the first time. I am also new to NET. I am using an HTTPPOST to get data from the front end in the format
amount : string;
currency : string;
description : string;
token : string;
name: string;
address_city: string;
address_line1: string;
address_line2: string;
address_state: string;
address_zip: string;
address_country: string;
I have stripe.cs and stripeDto.cs files:
public class Striper
{
public object Amount { get; set; }
public string Currency { get; set; }
public object Description { get; set; }
public string Token { get; set; }
public string Name { get; set; }
public string Address_line1 { get; set; }
public string Address_line2 { get; set; }
public string Address_state { get; set; }
public string Address_zip { get; set; }
public string Address_country { get; set; }
}
stripeDto:
public class StripeDto
{
public object Amount { get; set; }
public string Currency { get; set; }
public object Description { get; set; }
public string Token { get; set; }
public string Name { get; set; }
public string Address_line1 { get; set; }
public string Address_line2 { get; set; }
public string Address_state { get; set; }
public string Address_zip { get; set; }
public string Address_country { get; set; }
}
This is the mapping profile file:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Striper, StripeDto>();
CreateMap<StripeDto, Striper>();
}
}
Finally this is the Controller:
private readonly AppDbContext _context;
private IMapper _mapper;
public StripeController(AppDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<IActionResult> PostCreditCardData([FromBody] StripeDto stripeDto)
{
Console.WriteLine("got this from the front end", stripeDto);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.StripeDto.Add(stripeDto);
// Instantiate source object stripe
await _context.SaveChangesAsync();
_striper = _mapper.Map<Striper>(stripeDto);
return Ok(_striper);
}
I get this error in visual studio "Unable to resolve service for type 'myBackEnd.Entities.Striper'"
Here is the startup.cs code:
services.AddAutoMapper();
Your AutoMapper configuration in Startup class should be as follows:
public void ConfigureServices(IServiceCollection services)
{
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
//........
}
First, you must install Automapper dependency injection package:
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Call services.AddAutoMapper() in ConfigureServices method in the Startup class.
More on this at:
https://dotnetcoretutorials.com/2017/09/23/using-automapper-asp-net-core/
I know it's too late
but it's because of your startup and AutoMapper configuration
services.AddAutoMapper(typeof(MappingProfile).Assembly);
best regards
I recently implemented OData in my ASP .NET Core web API. I have found success as long as I am returning the database models directly. I run into trouble, however, as soon as I attempt to return domain models instead.
The underlying issue involves mapping a data class to a domain class while maintaining the IQueryable return type. While I have found partial success using AutoMapper's MapTo extension method, I find that I am unsuccessful when using the $extend method to expand a collection of entities that are also domain objects.
I have created a sample project to illustrate this issue. You may view or download the full project on github here. See the description below.
Given the following two database classes:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Order> Orders { get; set; }
public Product() {
Orders = new Collection<Order>();
}
}
public class Order
{
public int Id { get; set; }
public Double Price { get; set; }
public DateTime OrderDate { get; set; }
[Required]
public int ProductId { get; set; }
public Product Product { get; set; }
}
And the following domain models...
public class ProductEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<OrderEntity> Orders { get; set; }
}
public class OrderEntity
{
public int Id { get; set; }
public Double Price { get; set; }
public DateTime OrderDate { get; set; }
[Required]
public int ProductId { get; set; }
public Product Product { get; set; }
}
And the Products Controller
public class ProductsController
{
private readonly SalesContext context;
public ProductsController(SalesContext context) {
this.context = context;
}
[EnableQuery]
public IQueryable<ProductEntity> Get() {
return context.Products
.ProjectTo<ProductEntity>()
.AsQueryable();
}
}
All the following OData queries Pass:
http://localhost:51004/odata/Products
http://localhost:51004/odata/Orders
http://localhost:51004/odata/Orders?$expand=Products
The following query, however, does not pass:
http://localhost:51004/odata/Products?$expand=Orders
An HTTP response is never returned. The only failure message I get comes from the console:
System.InvalidOperationException: Sequence contains no matching element at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
Finally, here is a reference to the mapping profile:
public static class MappingProfile
{
public static void RegisterMappings() {
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Order, OrderEntity>();
cfg.CreateMap<Product, ProductEntity>();
});
}
}
I can solve the issue by simply returning a List instead of an IEnumerable in the controller, but this of course would trigger a large query against the database that would be performance intensive.
As stated above, you can find a link to the full project on Github here. Let me know if you find any answers!
I was able to get this working with a few small revisions.
Updating the domain models:
public class ProductEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class OrderEntity
{
public int Id { get; set; }
public double Price { get; set; }
public DateTime OrderDate { get; set; }
[Required]
public int ProductId { get; set; }
public Product Product { get; set; }
}
Manually enabling expansion on the route builder:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, SalesModelBuilder modelBuilder)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc(routeBuilder =>
{
routeBuilder.Expand().Select();
routeBuilder.MapODataServiceRoute("ODataRoutes", "odata",
modelBuilder.GetEdmModel(app.ApplicationServices));
});
}
Using the following Queries:
http://localhost:51004/odata/Products
http://localhost:51004/odata/Orders
http://localhost:51004/odata/Orders?$expand=Product
http://localhost:51004/odata/Products?$expand=Orders