Masstransit saga with exceptions - exception

I want to use masstransit saga in my .net core project but I could not find any documentations or (best practice)examples to handling exception.
If I have an error in my consumer,should I publish some failed event and consume that. Actually I don't think it is a good solution. So I want to throw exception and catch in Masstransit Statemachine.
OrderReceivedConsumer.cs
public class OrderReceivedConsumer : IConsumer<IOrderReceivedEvent>
{
public async Task Consume(ConsumeContext<IOrderReceivedEvent> context)
{
var orderCommand = context.Message;
await Console.Out.WriteLineAsync($"Order code: {orderCommand.OrderCode} Order id: {orderCommand.OrderId} is received.");
//do something..
throw new Exception("test");
//await context.Publish<IOrderProcessedEvent>(
// new { CorrelationId = context.Message.CorrelationId, OrderId = orderCommand.OrderId });
}
}
OrderSaga.cs
public class OrderSaga : MassTransitStateMachine<OrderSagaState>
{
public State Received { get; set; }
public State Processed { get; set; }
public Event<IOrderCommand> OrderCommand { get; set; }
public Event<IOrderProcessedEvent> OrderProcessed { get; set; }
public OrderSaga()
{
InstanceState(s => s.CurrentState);
Event(() => OrderCommand,
cec =>
cec.CorrelateBy(state => state.OrderCode, context => context.Message.OrderCode)
.SelectId(selector => Guid.NewGuid()));
Event(() => OrderProcessed, cec => cec.CorrelateById(selector =>
selector.Message.CorrelationId));
Initially(
When(OrderCommand)
.Then(context =>
{
context.Instance.OrderCode = context.Data.OrderCode;
context.Instance.OrderId = context.Data.OrderId;
})
.ThenAsync(
context => Console.Out.WriteLineAsync($"{context.Data.OrderId} order id is received..")
)
.TransitionTo(Received)
.Publish(context => new OrderReceivedEvent(context.Instance))
);
During(Received,
When(OrderProcessed)
.ThenAsync(
context => Console.Out.WriteLineAsync($"{context.Data.OrderId} order id is processed.."))
.Finalize()
);
SetCompletedWhenFinalized();
}
}
Where should I use the Automatonymous library "Catch" method?

Related

Can't seem to return my model using ASP.NET MVC and Entity Framework

This is my code, built on ASP.NET MVC and Entity Framework:
[HttpPost]
[Route("DeskBooking")]
public JsonResult DeskBooking(string dpStart, string dpEnd, int tmStart, int tmEnd)
{
DateTime dpStartCon = DateTime.Parse(GetDateStart(dpStart));
DateTime dpEndCon = DateTime.Parse(GetDateEnd(dpEnd));
using (Models.nhsdmsEntities ctx = new Models.nhsdmsEntities())
{
List<Models.tblDeskBooking> tblDB = ctx.tblDeskBookings
.Where(x => dpStartCon <= x.DateStart &&
x.DateEnd <= dpEndCon &&
tmStart >= x.TimeStart &&
tmEnd <= x.TimeEnd).ToList();
return Json(new { data = tblDB }, JsonRequestBehavior.AllowGet);
}
}
The tblDB has 3 rows but still on the client side I get this error:
An unhandled exception was generated during the execution of the current web request
[ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.]
Client-side code:
$(document).on("click", "#btnBookDeskEmp", function () {
var dpStart = $("#dpStart").val();
var dpEnd = $("#dpEnd").val();
var tmStart = $("#tmStart").val();
var tmEnd = $("#tmEnd").val();
AjaxReq(function (data) {
}, "DeskBooking", { dpStart: dpStart, dpEnd: dpEnd, tmStart: parseInt(tmStart), tmEnd: parseInt(tmEnd) });
})
function AjaxReq(callback, action, data) {
$.ajax({
url: "/Home/" + action,
method: "POST",
data: data,
})
.done(function (data) {
callback(data);
})
.fail(function (e) {
alert("error");
})
.always(function () {
console.log("complete");
});
}
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace NHSDMS.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class nhsdmsEntities : DbContext
{
public nhsdmsEntities()
: base("name=nhsdmsEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<tblDesk> tblDesks { get; set; }
public virtual DbSet<tblRoom> tblRooms { get; set; }
public virtual DbSet<tblDeskBooking> tblDeskBookings { get; set; }
}
}
In the edmx file, i had to delete everything from the navigation properties as this was messing up my namesspace. if you would like more info i can show sceenshots.

The instance of entity type 'Decks' cannot be tracked because another instance with the key value '{CardId: 578}' is already being tracked

im new to EF-Core and trying around. I´m stuck on a Problem where i want to Update or Insert Entries in a MySQL Table.
The Model looks like this:
public partial class Decks
{
public int DecklistId { get; set; }
public int CardId { get; set; }
public int Amount { get; set; }
public bool Maindeck { get; set; }
public virtual Cards Card { get; set; }
}
}
The Entity Configuration is the following:
modelBuilder.Entity<Decks>(entity =>
{
entity.HasKey(e => new { e.DecklistId, e.CardId, e.Maindeck })
.HasName("PRIMARY"); ;
entity.HasIndex(e => e.CardId);
entity.ToTable("decks");
entity.Property(e => e.DecklistId).HasColumnName("decklistID");
entity.Property(e => e.CardId).HasColumnName("cardID");
entity.Property(e => e.Maindeck).HasColumnName("maindeck");
entity.Property(e => e.Amount).HasColumnName("amount");
entity.HasOne(e => e.Card)
.WithOne()
.HasPrincipalKey<Decks>(b => b.CardId);
});
Atleast in my Repo im using this Code to find out if the Data should be updated or inserted.
public async Task<StandardResponse> UpdateDeck(List<Decks> deckCards)
{
StandardResponse errors = new StandardResponse { message = null, status = 200 };
try
{
deckCards.ForEach(x =>
{
Decks existingCard = _context.Decks.Find(x.DecklistId,x.CardId, x.Maindeck);
if (existingCard == null)
{
_context.Decks.Add(x); <---- Here occurs the Error
}
else
{
_context.Entry(existingCard).CurrentValues.SetValues(x);
}
_context.SaveChanges();
});
}
catch (DbUpdateException ex)
{
return new StandardResponse { message = ex, status = 501 };
}
return new StandardResponse { message = "Deck updated!", status = 200 };
}
The Problem appears, when i have to Object that have the same DecklistId and the same CardId. Even if the Maindeck property is different i get the error and i cant explain myself why...
Here is an List of Objects i want to Insert/Update.
The first Object will be added. The Second Object is of course not found on the database and at the Add-Statement the error occurs.
[
{
"decklistId": 28,
"cardId": 578,
"amount": 4,
"maindeck": true
},
{
"decklistId": 28,
"cardId": 578,
"amount": 4,
"maindeck": false
}
]
What could it be?
Thanks for helping guys!

HttpPost with JSON parameter is not working in ASP.NET Core 3

So, I migrated my RestAPI project to ASP.NET Core 3.0 from ASP.NET Core 2.1 and the HttpPost function that previously worked stopped working.
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody]Application login)
{
_logger.LogInfo("Starting Login Process...");
IActionResult response = Unauthorized();
var user = AuthenticateUser(login);
if (user != null)
{
_logger.LogInfo("User is Authenticated");
var tokenString = GenerateJSONWebToken(user);
_logger.LogInfo("Adding token to cache");
AddToCache(login.AppName, tokenString);
response = Ok(new { token = tokenString });
_logger.LogInfo("Response received successfully");
}
return response;
}
Now, the login object has null values for each property. I read here, that
By default, when you call AddMvc() in Startup.cs, a JSON formatter, JsonInputFormatter, is automatically configured, but you can add additional formatters if you need to, for example to bind XML to an object.
Since AddMvc was removed in aspnetcore 3.0, now I feel this is why I am unable to get my JSON object anymore. My Startup class Configure function looks like this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseRouting();
//app.UseAuthorization();
//app.UseMvc(options
// /*routes => {
// routes.MapRoute("default", "{controller=Values}/{action}/{id?}");
//}*/);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
The request I am sending through postman (raw and JSON options are selected)
{
"AppName":"XAMS",
"licenseKey": "XAMSLicenseKey"
}
UPDATES
Postman Header: Content-Type:application/json
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//_logger.LogInformation("Starting Log..."); //shows in output window
services.AddSingleton<ILoggerManager, LoggerManager>();
services.AddMemoryCache();
services.AddDbContext<GEContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddControllers();
services.AddRazorPages();
//Authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "https://localhost:44387/";
options.Audience = "JWT:Issuer";
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
options.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("GuidelineReader", p => {
p.RequireClaim("[url]", "GuidelineReader");
});
});
//
}
Application.cs
public class Application
{
public string AppName;
public string licenseKey;
}
With you updated code, I think the reason is you didn't create setter for your properties.
To fix the issue, change your Application model as below:
public class Application
{
public string AppName {get;set;}
public string licenseKey {get;set;}
}

How to change cas resulting JSON objects back to PascalCase?

I am writing a WebAPICore to return the JSON objects from the database. For unknown reason, the properties are returned as camelCase by default.
I have checked the SQL Script and it does return the correct case for the DataFields. But when I consume the service, the properties of the objects are changed to camelCase automatically.
For example, OfferingID is returned as offeringID
The existing Return JSON object
{
"offeringID": 120842,
"courseCode": "FLTE2A1F/1",
"courseName": "FLT - E2 Certificate in Skills for Working Life (Animals) (QCF)"
}
The format which I want to return
{
"OfferingID": 120842,
"CourseCode": "FLTE2A1F/1",
"CourseName": "FLT - E2 Certificate in Skills for Working Life (Animals) (QCF)"
}
The Model - Offering:
public class Offering
{
[Key]
public int OfferingID { get; set; }
public string CourseCode { get; set; }
public string CourseName { get; set; }
}
My WebAPI Controller Get Method
[HttpGet("{id}")]
public async Task<IActionResult> GetOfferingDetail(int id)
{
var obj = await _context.Offerings.FromSql("dbo.GetOfferingDetail #p0", id).SingleOrDefaultAsync();
if (obj == null)
return NotFound("ID not found");
return new ObjectResult(obj);
}
Configure Services Method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContexts.OakCommonsDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyConnection")));
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()));
var mvccore = services.AddMvc();
mvccore.AddJsonOptions(o => o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore);
}
Could you please advise me how I could return JSON Objects in the Exact Case as I defined in the Model?
Here is the working code. By default, WebAPI Core is going to use CamelCasePropertyNamesContractResolver(). You need to change it to DefaultContractResolver to render as you defined in the Model.
And DefaultContractResolver is under Newtonsoft.Json.Serialization namespace.
services.AddMvc()
.AddJsonOptions(o => o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)
.AddJsonOptions(o => o.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver());

Populating IEnumerable<class> with JSON result

I previously had the following line of code from within my AdminController that was successfully returning a list of relevant subsections from within a course:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetCourseSections(int courseID)
{
var Sections = dbcontext.CourseSection.Where(cs => cs.CourseID.Equals(courseID)).Select(x => new
{
sectionID = x.CourseSectionID,
sectionTitle = x.Title
);
return Json(Sections, JsonRequestBehavior.AllowGet);
}
I was informed to take this out of the controller as it was bad practice to call dbcontext and so i moved this to the AdminViewModel. Within my AdminViewModel I have a variable public List CourseSectionList { get; set; } and I am trying to populate this variable with the JSON request details. My code is as follows:
AdminViewModel
public void GetCourseSectionDetails(int courseID)
{
var Sections = dbcontext.CourseSection.Where(cs => cs.CourseID.Equals(courseID)).Select(x => new CourseSection
{
CourseSectionID = x.CourseSectionID,
Title = x.Title
});
this.CourseSectionList = Sections.ToList();
}
AdminController
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetCourseSections(int courseID)
{
avm.GetCourseSectionDetails(courseID);
var Sections = avm.CourseSectionList.Where(cs => cs.CourseID.Equals(courseID)).Select(x => new
{
sectionID = x.CourseSectionID,
sectionTitle = x.Title
});
System.Diagnostics.EventLog.WriteEntry("Application", "JSON=" + Sections.ToList(), System.Diagnostics.EventLogEntryType.Error);
return Json(Sections, JsonRequestBehavior.AllowGet);
}
I am getting the error The entity or complex type 'MetaLearning.Data.CourseSection' cannot be constructed in a LINQ to Entities query. How can I populate this.CourseSectionList variable using the Sections?
As pointed by your error message, you can't, in linq to entities, use a
.Select(m => new <Entity>{bla bla})
where <Entity>... is one of your model's entity.
So either you use a "non model" class (DTO), which has the properties you need, or you have to enumerate before selecting (because linq to objects has not that limitation)
.ToList()
.Select(m => new <Entity>{bla bla});
You can find some nice explanations of why it's not possible here
EDIT :
you may also do something like that, if you wanna retrive only some properties of your entity, and don't wanna use a DTO :
return ctx
.CourseSection
.Where(cs => cs.CourseID.Equals(courseID))
//use an anonymous object to retrieve only the wanted properties
.Select(x => new
{
c= x.CourseSectionID,
t= x.Title,
})
//enumerate, good bye linq2entities
.ToList()
//welcome to linq2objects
.Select(m => new CourseSection {
CourseSectionID = m.c,
Title = m.t,
})
.ToList();
You don't need to repeat the same code in the controller, but directly pass the list to the view.
This being said I am informing you that placing data access code in your view model is even worse practice than keeping it in the controller. I would recommend you having a specific DAL layer:
public interface IRepository
{
public IList<CourseSection> GetSections(int courseID);
}
which would be implemented:
public class RepositoryEF : IRepository
{
public IList<CourseSection> GetSections(int courseID)
{
using (ctx = new YourDbContextHere())
{
return ctx
.CourseSection
.Where(cs => cs.CourseID.Equals(courseID))
.Select(x => new CourseSection
{
CourseSectionID = x.CourseSectionID,
Title = x.Title,
})
.ToList();
}
}
}
and finally have your controller take the repository as dependency:
public class SomeController : Controller
{
private readonly IRepository repo;
public SomeController(IRepository repo)
{
this.repo = repo;
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetCourseSections(int courseID)
{
var sections = this.repo.GetSections(courseID);
return Json(sections, JsonRequestBehavior.AllowGet);
}
}
I did this as follows using Darin's answer as a guide:
ViewModel
public void GetCourseSectionDetails(int courseID)
{
this.CourseSectionList = dbcontext.CourseSection.AsEnumerable().Where(cs => cs.CourseID.Equals(courseID)).Select(x => new CourseSection
{
CourseSectionID = x.CourseSectionID,
Title = x.Title
}).ToList();
}
Controller
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetCourseSections(int courseID)
{
var sections = avm.CourseSectionList;
return Json(sections, JsonRequestBehavior.AllowGet);
}