firebase repository in .net core 3.1 does not work on the server when i deploy it, and works well locally - firebase

i want to send commend in Firestore using a API .net core 3.1, and i am working using clean architecture. and an CQRS pattern.
like this:
namespace SmartRestaurant.Infrastructure.Services
{
public class FirebaseConfig
{
public string BasePath { get; set; }
}
public class FirebaseRepository : IFirebaseRepository
{
readonly string _DataBaseBasepath;
readonly FirestoreDb _db;
public FirebaseRepository(IOptions<FirebaseConfig> conf)
{
_DataBaseBasepath = conf.Value.BasePath;
if (string.IsNullOrEmpty(_DataBaseBasepath))
{
throw new ArgumentNullException("fireBase Path not found in appsettings");
}
var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
_db = FirestoreDb.Create("^projectName");
}
public async Task<T> AddAsync<T>(string path, T data, CancellationToken cancellationToken)
{
try
{
DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
var objectTosend = getOrderToDictionary(data);
await doc.SetAsync(objectTosend, null, cancellationToken);
return data;
}
catch (Exception exe)
{
throw exe;
}
}
public async Task<T> UpdateAsync<T>(string path, T data, CancellationToken cancellationToken)
{
try
{
DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
var objectTosend = getOrderToDictionary(data);
await doc.UpdateAsync(objectTosend, null, cancellationToken);
return data;
}
catch (Exception exe)
{
throw exe;
}
}
}
}
that code is in the infrastrecture layer and the interface of this service is in the application layer
public interface IFirebaseRepository
{
Task<T> AddAsync<T>(string path,T data,CancellationToken cancellationToken);
Task<T> UpdateAsync<T>(string path,T data, CancellationToken cancellationToken);
}
the injection of this service is in the infrastrecture layer like this
services.AddTransient < IFirebaseRepository, FirebaseRepository> ();
i use this service in the application layer like this :
public class OrdersCommandsHandlers : IRequestHandler<CreateOrderCommand, OrderDto>,
IRequestHandler<UpdateOrderCommand, NoContent>,
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;
private readonly IUserService _userService;
private readonly IFirebaseRepository _fireBase;
private readonly string CreateAction = "CreateAction";
private readonly string UpdateAction = "UpdateAction";
public OrdersCommandsHandlers(IApplicationDbContext context,
IMapper mapper,
IUserService userService,
IFirebaseRepository fireBase)
{
_context = context;
_mapper = mapper;
_userService = userService;
_fireBase = fireBase;
}
public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var validator = new CreateOrderCommandValidator();
var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
if (!result.IsValid) throw new ValidationException(result);
....
await _context.SaveChangesAsync(cancellationToken);
var orderDto = _mapper.Map<OrderDto>(order);
orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
var path = request.FoodBusinessId + "/Orders/" + orderDto.OrderId;
await _fireBase.AddAsync(path, orderDto, cancellationToken);
......
return _mapper.Map<OrderDto>(newOrder);
}
public async Task<NoContent> Handle(UpdateOrderCommand request, CancellationToken cancellationToken)
{
var validator = new UpdateOrderCommandValidator();
var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
if (!result.IsValid) throw new ValidationException(result);
......
var orderDto = _mapper.Map<OrderDto>(order);
var foodBusiness = await _context.FoodBusinesses.FindAsync(order.FoodBusinessId);
if (foodBusiness != null)
orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
var path = order.FoodBusinessId + "/Orders/" + order.OrderId;
await _fireBase.UpdateAsync(path,orderDto, cancellationToken);
return default;
}
}
when i deploiye this code in the server i have this erreur :
Error constructing handler for request of type MediatR.IRequestHandler`2[SmartRestaurant.Application.Orders.Commands.CreateOrderCommand,SmartRestaurant.Application.Common.Dtos.OrdersDtos.OrderDto]. Register your handlers with the container. See the samples in GitHub for examples.
i try to using fireStor in the NetCore 3 api with CQRS, and a erreur Appear when i use the hendler that contaie firebase service.
and locally all work well , the probleme appear in the server when i deploie the code.

the reason of this issues is in the constructor of firebaseService
public FirebaseRepository(IOptions<FirebaseConfig> conf)
{
_DataBaseBasepath = conf.Value.BasePath;
if (string.IsNullOrEmpty(_DataBaseBasepath))
{
throw new ArgumentNullException("fireBase Path not found in appsettings");
}
var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
_db = FirestoreDb.Create("^projectName");
}
the conf.Value.BasePath is null in the server because the appsetting.json does not contain the FirebaseConfig section.

Related

Asp.Net Core Web API entity Framework connect to two databases

I am doing an Asp.Net Core API and I am connecting to a two databases using EF setted in appsettings.json
"ConnectionStrings": {
"DBConnection": "Server=2679; Database=A; Trusted_Connection=true; MultipleActiveResultSets=true; Integrated Security=true;Encrypt=false;",
"DBConnection2": "Server= 2684; Database=B; Trusted_Connection=true; MultipleActiveResultSets=true; Integrated Security=true;Encrypt=false;"
}
In my Program.cs I have setted this two connections
var connectionString = (builder.Configuration.GetConnectionString("DBConnection") ?? String.Empty).Trim();
var connectionString2 = (builder.Configuration.GetConnectionString("DBConnectionAnthem") ?? String.Empty).Trim();
builder.Services.ConfigureServices(connectionString);
builder.Services.ConfigureServices(connectionString2);
I call ConfigureServices with both connections and looks like this
public static class Configure
{
public static void ConfigureServices(this IServiceCollection services, string connectionString)
{
services
.AddDbContext<CobraDbContext>(options => options.UseSqlServer(connectionString));
........
services.AddScoped<IUnitOfWork, UnitOfWork>();
}
}
}
I am using EF and I have defined my DbContext like this
public class CobraDbContext : DbContext
{
public CobraDbContext(DbContextOptions<CobraDbContext> options)
: base(options)
{
}
public DbSet<SearchResultModel> ParticipantSearch { get; set; } = null!;
....
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
}
From My Controller Method I call the Service.cs witch use UnitOfwork
public class ParticipantService : IParticipantService
{
private readonly ILogger<ParticipantService> _logger;
private readonly IUnitOfWork _iUnitOfwork;
public ParticipantService(ILogger<ParticipantService> logger, IUnitOfWork iUnitOfwork)
{
_logger = logger;
_iUnitOfwork = iUnitOfwork;
}
public async Task<HttpResponseMessage> Search(string participantId)
{
try
{
List<SearchResultModel>? search = await _iUnitOfwork.Participant.AAA(participantId);
return Request.CreateResponse(HttpStatusCode.OK, search);
}
catch (Exception ex)
{
}
}
From My Service I call the Repository that have a generic repository
public class ParticipantRepository : GenericRepository<SearchResultModel>, IParticipantRepository
{
private readonly CobraDbContext _db;
public ParticipantRepository(CobraDbContext db) : base(db)
{
_db = db;
}
public async Task<List<ParticipantPlanModel>?> AAA(string participantId)
{
Query participantGetByID = new();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
participantGetByID.SelectFrom = " exec sp";
List<ParticipantPlanModel>? _return = await ExecuteGeneric(participantGetByID);
return _return;
}
}
I have my generic repo like this
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly CobraDbContext Context;
internal DbSet<T> dbSet;
public GenericRepository(CobraDbContext context)
{
Context = context;
dbSet = context.Set<T>();
}
public async Task<List<T>?> ExecuteGeneric(Query query)
{
// var defaultVal = default(T);
var cParameters = new SqlParameter[query.Parameters?.Count ?? 0];
if (query.Parameters != null)
{
int i = 0;
foreach (KeyValuePair<string, string> _param in query.Parameters)
{
cParameters[i] = new SqlParameter() { ParameterName = _param.Key, Value = _param.Value };
i++;
}
}
return await Context.Set<T>().FromSqlRaw(query.SelectFrom + query.Where + query.OrderBy, cParameters).ToListAsync();
}
Depending on the parameter I have to call a database or a the another. I know I can do this duplicating almost all the code... Having to DbContext and two generic Repo..
Is there a way to simplify it and not replicate most of the code?
Thanks

Quartz .NET The instance of entity type 'TABLENAME' cannot be tracked because

We have built an API with .NET Core 3.1 that extracts data from an Excel and stores it via
EF Core into a MS SQL database. We use Quartz. NET so that it is handled in a background thread. For DI we use Autofac.
We use Scoped Services to be able to use the DBContext via DI (as described here https://andrewlock.net/creating-a-quartz-net-hosted-service-with-asp-net-core/).
Unfortunately, saving the data still does not work when multiple users are using the application at the same time. We get the following error message:
The instance of entity type 'TABLENAME' cannot be tracked because another instance with the same key value for {'TABLEKEY'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Here our related code:
Startup.cs
// Add DbContext
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default"), b => b.MigrationsAssembly("XY.Infrastructure")));
// Add Quartz
services.AddQuartz(q =>
{
// as of 3.3.2 this also injects scoped services (like EF DbContext) without problems
q.UseMicrosoftDependencyInjectionJobFactory();
// these are the defaults
q.UseSimpleTypeLoader();
q.UseDefaultThreadPool(tp =>
{
tp.MaxConcurrency = 24;
});
});
services.AddQuartzServer(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true;
});
// Add Services
services.AddHostedService<QuartzHostedService>();
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
services.AddTransient<ImportJob>();
services.AddScoped<IApplicationDbContext, ApplicationDbContext>();
services.AddScoped<IMyRepository, MyRepository>();
Logic.cs
// Grab the Scheduler instance from the Factory
var factory = new StdSchedulerFactory();
var scheduler = await factory.GetScheduler();
var parameters = new JobDataMap()
{
new KeyValuePair<string, object>("request", message),
new KeyValuePair<string, object>("sales", sales),
};
var jobId = $"processJob{Guid.NewGuid()}";
var groupId = $"group{Guid.NewGuid()}";
// defines the job
IJobDetail job = JobBuilder.Create<ImportJob>()
.WithIdentity(jobId, groupId)
.UsingJobData(parameters)
.Build();
// defines the trigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity($"Trigger{Guid.NewGuid()}", groupId)
.ForJob(job)
.StartNow()
.Build();
// schedule Job
await scheduler.ScheduleJob(job, trigger);
// and start it off
await scheduler.Start();
QuartzHostedService.cs
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly ILogger<QuartzHostedService> _logger;
private readonly IEnumerable<JobSchedule> _jobSchedules;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IJobFactory jobFactory,
IEnumerable<JobSchedule> jobSchedules,
ILogger<QuartzHostedService> logger)
{
_schedulerFactory = schedulerFactory;
_jobSchedules = jobSchedules;
_jobFactory = jobFactory;
_logger = logger;
}
public IScheduler Scheduler { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
foreach (var jobSchedule in _jobSchedules)
{
var job = CreateJob(jobSchedule);
var trigger = CreateTrigger(jobSchedule);
await Scheduler.ScheduleJob(job, trigger, cancellationToken);
}
await Scheduler.Start(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await Scheduler?.Shutdown(cancellationToken);
}
private static IJobDetail CreateJob(JobSchedule schedule)
{
var jobType = schedule.JobType;
return JobBuilder
.Create(jobType)
.WithIdentity(jobType.FullName)
.WithDescription(jobType.Name)
.Build();
}
private static ITrigger CreateTrigger(JobSchedule schedule)
{
return TriggerBuilder
.Create()
.WithIdentity($"{schedule.JobType.FullName}.trigger")
.StartNow()
.Build();
}
}
SingletonJobFactory.cs
public class SingletonJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public SingletonJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
}
catch (Exception ex)
{
throw;
}
}
public void ReturnJob(IJob job) { }
}
Importjob.cs
[DisallowConcurrentExecution]
public class ImportJob : IJob
{
private readonly IServiceProvider _provider;
private readonly ILogger<ImportJob> _logger;
public ImportJob(IServiceProvider provider, ILogger<ImportJob> logger)
{
_provider = provider;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
try
{
using (var scope = _provider.CreateScope())
{
var jobType = context.JobDetail.JobType;
var job = scope.ServiceProvider.GetRequiredService(jobType) as IJob;
var repo = _provider.GetRequiredService<MYRepository>();
var importFactSales = _provider.GetRequiredService<IImportData>();
var savedRows = 0;
var request = (MyRequest)context.JobDetail.JobDataMap.Get("request");
var sales = (IEnumerable<MyData>)context.JobDetail.JobDataMap.Get("sales");
await importFactSales.saveValidateItems(repo, request, sales, savedRows);
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}
I have found a solution in the meantime. As #marko-lahma described in the comment, use the built-in hosted service and don't implement your own JobFactory.
Remove the SingletonJobFactory.cs and QuartzHostedService.cs
Use the Autofac.Extras.Quartz and Quartz.Extensions.Hosting Nuget Package
Don't use CreateScope anymore, inject all needed Dependencies over the Constructor
Register QuartzAutofacFactoryModule and QuartzAutofacJobsModule in the Startup.

Call to DB gets stuck on await (but works when called from a Blazor component)

When calling the method from the .cs file, the app gets stuck on the await line; but if I call it from the .razor it works flawlessy.
.cs
public AccountService(IUserData userData)
{
_userData = userData;
}
...
public async Task<bool> Validate(string userId, string password)
{
...
try
{
List<UserModel> users = new List<UserModel<();
users = await _userData.GetUsers();
//NEVER GETS HERE
return true;
}
catch (Exception ex)
{
return false;
}
...
}
.razor
#inject IUserData _db;
#code {
private List<UserModel> users;
...
protected override async Task OnInitializedAsync()
{
users = await _db.GetUsers();
}
...
UserData
public class UserData : IUserData
{
private readonly ISqlDataAccess _db;
public UserData(ISqlDataAccess db)
{
_db = db;
}
public Task<List<UserModel>> GetUsers()
{
string sql = "Select *from dbo.Users";
return _db.LoadData<UserModel, dynamic>(sql, new { });
}
...
}
IUserData
public interface IUserData
{
Task<List<UserModel>> GetUsers();
...
}
DBAccess
public async Task<List<T>> LoadData<T, U>(string sql, U parameters)
{
string connectionString = _config.GetConnectionString(ConnectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var data = await connection.QueryAsync<T>(sql, parameters); //I GET STUCK HERE
return data.ToList();
}
}
IDBAccess
Task<List<T>> LoadData<T, U>(string sql, U parameters);
PS
I updated this post https://stackoverflow.com/questions/68225154/implementing-an-interface-on-a-class-with-dependency-injection with this question, but sinced I had already marked it as answered I decided to make a new one
Your problem is how your code is calling the asynchronous method:
if (((AccountService)AccountService).Validate(user.UserCredentials, user.Password).Result)
The .Result may seem weird, but otherwise I get an error: can't convert ...Task to bool
The proper solution for this error is to use await, not Result:
if (await ((AccountService)AccountService).Validate(user.UserCredentials, user.Password))
Using Result can cause deadlocks.

how to properly implement Microsoft.IdentityModel.Clients.ActiveDirectory in xamarin 4

After install the nuget package Microsoft.IdentityModel.Clients.ActiveDirectory
I try to acquire token via
string cloud = "https://login.microsoftonline.com/common/oauth2";
string tenantId = App.tenantId;
string authority = $"{cloud}/{tenantId}";
//
string clientId = App.clientId;
Uri redirectUri = App.redirectUrl;
string resource = clientId;
AuthenticationResult authResult = null;
AuthenticationContext authContext = new AuthenticationContext(authority);
try
{
if (authContext.TokenCache.ReadItems().Count() > 0)
{
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
authResult = await authContext.AcquireTokenSilentAsync(resource, clientId);
}
else
{
authResult = await authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
}
}
catch (AdalSilentTokenAcquisitionException ee)
{
authResult = await authContext.AcquireTokenAsync(resource,clientId, redirectUri,null);
}
When i try to build i get the following errors
cannot convert from 'Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior' to 'Android.App.Activity'
cannot convert from 'Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior' to 'UIKit.UIViewController'
the line causing this is line that trigger the error
authResult = await authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
how do i solve this ?
Xamarin version is 4.0.0.482894
Microsoft.IdentityModel.Clients.ActiveDirectory version 5.1.0
Visual studio 2017
In your Shared project, use IPlatformParameter , something like this:
public class AuthenticationManager
{
private static string _authority;
private static string _resource;
private static string _clientId;
private static string _returnUri;
private static IPlatformParameters _parameters;
private string _accessToken;
public static UserInfo UserInfo { get; private set; }
public static void SetConfiguration(string authority, string resource, string clientId, string returnUri)
{
_authority = authority;
_resource = resource;
_clientId = clientId;
_returnUri = returnUri;
var authContext = new AuthenticationContext(_authority);
authContext.TokenCache.Clear();
}
public static void SetParameters(IPlatformParameters parameters)
{
_parameters = parameters;
}
public async Task<bool> LoginAsync()
{
_accessToken = await GetAccessTokenAsync();
return true;
}
public Task LogoutAsync()
{
var authContext = new AuthenticationContext(_authority);
var cachedToken = authContext.TokenCache.ReadItems().FirstOrDefault(t => t.Authority == _authority && t.ClientId == _clientId && t.Resource == _resource);
if (cachedToken != null)
{
authContext.TokenCache.DeleteItem(cachedToken);
}
UserInfo = null;
_accessToken = null;
return Task.CompletedTask;
}
private async Task<string> GetAccessTokenAsync()
{
var uri = new Uri(_returnUri);
var authContext = new AuthenticationContext(_authority);
var authResult = await authContext.AcquireTokenAsync(_resource, _clientId, uri, _parameters);
UserInfo = authResult.UserInfo;
return authResult.AccessToken;
}
}
Then in platform specific, call SetParameters method:
Example in Android on your MainActivity.cs :
protected override void OnCreate(Bundle bundle)
{
var platformParameters = new PlatformParameters(this);
AuthenticationManager.SetParameters(platformParameters);
}
Similarly on iOS:
public override void ViewDidLoad()
{
var platformParameters = new PlatformParameters(this);
AuthenticationManager.SetParameters(platformParameters);
}
You need to cast it to Activity
new PlatformParameters((Activity) PromptBehavior.Auto));

How can I return a response in ASP.NET Core MVC middleware using MVC's content negotiation?

I have some ASP.NET Core MVC middleware to catch unhandled exceptions that I would like to return a response from.
While it is easy to just httpContext.Response.WriteAsync to write a string and e.g. use JsonSerializer to serialise an object to a string, I would like to use the standard serialisation settings and content negotiation so that if I change my default output formatting to XML or a text/xml accept header is sent when I have multiple output formatters configured then XML is returned, as it does if I return an ObjectResult from a controller.
Does anyone know how this can be achieved in middleware?
Here is my code so far which only writes JSON:
public class UnhandledExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly IOutputFormatter _outputFormatter;
private readonly IHttpResponseStreamWriterFactory _streamWriterFactory;
public UnhandledExceptionMiddleware(RequestDelegate next, JsonOutputFormatter outputFormatter, IHttpResponseStreamWriterFactory streamWriterFactory)
{
_next = next;
_outputFormatter = outputFormatter;
_streamWriterFactory = streamWriterFactory;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var error = new ErrorResultModel("Internal Server Error", exception.Message, exception.StackTrace);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await _outputFormatter.WriteAsync(new OutputFormatterWriteContext(context, _streamWriterFactory.CreateWriter, typeof(ErrorResultModel), error));
}
}
where ErrorResultModel is defined as:
public class ErrorResultModel
{
public string ResultMessage { get; };
public string ExceptionMessage { get; };
public string ExceptionStackTrace { get; };
public ErrorResultModel(string resultMessage, string exceptionMessage, string exceptionStackTrace)
{
ResultMessage = resultMessage;
ExceptionMessage = exceptionMessage;
ExceptionStackTrace = exceptionStackTrace;
}
}
This is not possible in ASP.NET Core 2.0 MVC.
This will be possible in 2.1:
public static class HttpContextExtensions
{
private static readonly RouteData EmptyRouteData = new RouteData();
private static readonly ActionDescriptor EmptyActionDescriptor = new ActionDescriptor();
public static Task WriteResultAsync<TResult>(this HttpContext context, TResult result)
where TResult : IActionResult
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var executor = context.RequestServices.GetService<IActionResultExecutor<TResult>>();
if (executor == null)
{
throw new InvalidOperationException($"No result executor for '{typeof(TResult).FullName}' has been registered.");
}
var routeData = context.GetRouteData() ?? EmptyRouteData;
var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);
return executor.ExecuteAsync(actionContext, result);
}
}
public class Program : StartupBase
{
public static Task Main(string[] args)
{
return BuildWebHost(args).RunAsync();
}
public static IWebHost BuildWebHost(string[] args)
{
return new WebHostBuilder().UseStartup<Program>().UseKestrel().Build();
}
public override void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().AddJsonFormatters();
}
public override void Configure(IApplicationBuilder app)
{
app.Use((ctx, next) =>
{
var model = new Person("Krisian", "Hellang");
var result = new ObjectResult(model);
return ctx.WriteResultAsync(result);
});
}
}
public class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; }
public string LastName { get; }
}

Resources