Hangfire AutomaticRetry Attempts attribute not set on method - hangfire

I am using Hangfire v1.6.14.0. I set the AutomaticRetry Attempts attribute on Method but the retry count is still the default 10. Is there something else that needs to be done?
[AutomaticRetry(Attempts = 3)]
public void SubmitRequest()
{
}

Related

How to set Duration dynamically for ResponseCacheAttribute

I have a .Net Core 3.1 Web Api application and use the ResponseCache attribute on a controller action.
[HttpGet]
[ResponseCache(Location = ResponseCacheLocation.Any, Duration = 30, VaryByQueryKeys = new[] { "id"})]
public string Get([FromQuery] int id)
{...}
While this is working fine with the hardcoded value for Duration, I need to set this dynamically from the config somehow.
I already tried:
Configuring it in Response Caching Middleware, but then it applies to all controller actions.
Deriving from ResponseCacheAttribute does not work, as it's internal.
Is there a simple way to achieve what I want, or do I have to write the whole thing (custom ResponseCacheAtrribute + ResponseCacheFilter + ResponseCacheFilterExecutor) by myself ?
I make a new attribute which inherits from the ResponseCacheAttribute which adds most of what I need. For example -
public class ResponseCacheTillAttribute : ResponseCacheAttribute
{
public ResponseCacheTillAttribute(ResponseCacheTime time = ResponseCacheTime.Midnight)
{
DateTime today = DateTime.Today;
switch (time)
{
case ResponseCacheTime.Midnight:
DateTime midnight = today.AddDays(1).AddSeconds(-1);
base.Duration = (int)(midnight - DateTime.Now).TotalSeconds;
break;
default:
base.Duration = 30;
break;
}
}
}
The Enum looks like
public enum ResponseCacheTime
{
Midnight
}
This allows me to build in specific times that I may need. I haven't fully tested this but confirmed I see the information in the response output.
You should be able to add any arguments or information you need into the attribute.

Save complex object to session ASP .NET CORE 2.0

I am quite new to ASP .NET core, so please help. I would like to avoid database round trip for ASP .NET core application. I have functionality to dynamically add columns in datagrid. Columns settings (visibility, enable, width, caption) are stored in DB.
So I would like to store List<,PersonColumns> on server only for actual session. But I am not able to do this. I already use JsonConvert methods to serialize and deserialize objects to/from session. This works for List<,Int32> or objects with simple properties, but not for complex object with nested properties.
My object I want to store to session looks like this:
[Serializable]
public class PersonColumns
{
public Int64 PersonId { get; set; }
List<ViewPersonColumns> PersonCols { get; set; }
public PersonColumns(Int64 personId)
{
this.PersonId = personId;
}
public void LoadPersonColumns(dbContext dbContext)
{
LoadPersonColumns(dbContext, null);
}
public void LoadPersonColumns(dbContext dbContext, string code)
{
PersonCols = ViewPersonColumns.GetPersonColumns(dbContext, code, PersonId);
}
public static List<ViewPersonColumns> GetFormViewColumns(SatisDbContext dbContext, string code, Int64 formId, string viewName, Int64 personId)
{
var columns = ViewPersonColumns.GetPersonColumns(dbContext, code, personId);
return columns.Where(p => p.FormId == formId && p.ObjectName == viewName).ToList();
}
}
I would like to ask also if my approach is not bad to save the list of 600 records to session? Is it better to access DB and load columns each time user wants to display the grid?
Any advice appreciated
Thanks
EDIT: I have tested to store in session List<,ViewPersonColumns> and it is correctly saved. When I save object where the List<,ViewPersonColumns> is property, then only built-in types are saved, List property is null.
The object I want to save in session
[Serializable]
public class UserManagement
{
public String PersonUserName { get; set; }
public Int64 PersonId { get; set; }
public List<ViewPersonColumns> PersonColumns { get; set; } //not saved to session??
public UserManagement() { }
public UserManagement(DbContext dbContext, string userName)
{
var person = dbContext.Person.Single(p => p.UserName == userName);
PersonUserName = person.UserName;
PersonId = person.Id;
}
/*public void PrepareUserData(DbContext dbContext)
{
LoadPersonColumns(dbContext);
}*/
public void LoadPersonColumns(DbContext dbContext)
{
LoadPersonColumns(dbContext, null);
}
public void LoadPersonColumns(DbContext dbContext, string code)
{
PersonColumns = ViewPersonColumns.GetPersonColumns(dbContext, code, PersonId);
}
public List<ViewPersonColumns> GetFormViewColumns(Int64 formId, string viewName)
{
if (PersonColumns == null)
return null;
return PersonColumns.Where(p => p.FormId == formId && p.ObjectName == viewName).ToList();
}
}
Save columns to the session
UserManagement userManagement = new UserManagement(_context, user.UserName);
userManagement.LoadPersonColumns(_context);
HttpContext.Session.SetObject("ActualPersonContext", userManagement);
HttpContext.Session.SetObject("ActualPersonColumns", userManagement.PersonColumns);
Load columns from the session
//userManagement build-in types are set. The PersonColumns is null - not correct
UserManagement userManagement = session.GetObject<UserManagement>("ActualPersonContext");
//The cols is filled from session with 600 records - correct
List<ViewPersonColumns> cols = session.GetObject<List<ViewPersonColumns>>("ActualPersonColumns");
Use list for each column is better than use database.
you can't create and store sessions in .net core like .net framework 4.0
Try Like this
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//services.AddDbContext<GeneralDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc().AddSessionStateTempDataProvider();
services.AddSession();
}
Common/SessionExtensions.cs
sing Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IMAPApplication.Common
{
public static class SessionExtensions
{
public static T GetComplexData<T>(this ISession session, string key)
{
var data = session.GetString(key);
if (data == null)
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(data);
}
public static void SetComplexData(this ISession session, string key, object value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
}
}
Usage
==> Create Session*
public IActionResult Login([FromBody]LoginViewModel model)
{
LoggedUserVM user = GetUserDataById(model.userId);
//Create Session with complex object
HttpContext.Session.SetComplexData("loggerUser", user);
return Json(new { status = result.Status, message = result.Message });
}
==> Get Session data*
public IActionResult Index()
{
//Get Session data
LoggedUserVM loggedUser = HttpContext.Session.GetComplexData<LoggedUserVM>("loggerUser");
}
Hope this is helpful. Good luck.
This is an evergreen post, and even though Microsoft has recommended serialisation to store the object in session - it is not a correct solution unless your object is readonly, I have a blog explaining all scenario here and i have even pointed out the issues in GitHub of Asp.Net Core in issue id 18159
Synopsis of the problems are here:
A. Serialisation isn't same as object, true it will help in distributed server scenario but it comes with a caveat that Microsoft have failed to highlight - that it will work without any unpredictable failures only when the object is meant to be read and not to be written back.
B. If you were looking for a read-write object in the session, everytime you change the object that is read from the session after deserialisation - it needs to be written back to the session again by calling serialisation - and this alone can lead to multiple complexities as you will need to either keep track of the changes - or keep writing back to session after each change in any property. In one request to the server, you will have scenarios where the object is written back multiple times till the response is sent back.
C. For a read-write object in the session, even on a single server it will fail, as the actions of the user can trigger multiple rapid requests to the server and not more than often system will find itself in a situation where the object is being serialised or deserialised by one thread and being edited and then written back by another, the result is you will end up with overwriting the object state by threads - and even locks won't help you much since the object is not a real object but a temporary object created by deserialisation.
D. There are issues with serialising complex objects - it is not just a performance hit, it may even fail in certain scenario - especially if you have deeply nested objects that sometimes refer back to itself.
The synopsis of the solution is here, full implementation along with code is in the blog link:
First implement this as a Cache object, create one item in IMemoryCache for each unique session.
Keep the cache in sliding expiration mode, so that each time it is read it revives the expiry time - thereby keeping the objects in cache as long as the session is active.
Second point alone is not enough, you will need to implement heartbeat technique - triggering the call to session every T minus 1 min or so from the javascript. (This we anyways used to do even to keep the session alive till the user is working on the browser, so it won't be any different
Additional Recommendations
A. Make an object called SessionManager - so that all your code related to session read / write sits in one place.
B. Do not keep very high value for session time out - If you are implementing heartbeat technique, even 3 mins of session time out will be enough.

How do I achieve through Jboss Resteasy interceptors?

I am working on the Jboss Resteasy API to implement the REST services on Jboss server.I am new to this area. Can someone help me out here...
There is a Rest Service method with custom annotation(VRestAuto) like below.
#POST
#Produces("text/json")
#Path("/qciimplinv")
#Interceptors(VRestInterceptor.class)
public String getInvSummary(#VRestAuto("EnterpriseId") String enterpriseId,String circuitType){
....
businessMethod(enterpriseId,circuitType);
....
}
#VRestAuto annotation tell us 'enterpriseId' value is available in the user session.
User pass the circuitType alone as the POST parameter in the Rest Client tool.Should ideally read the enterpriseid from session and invoke the Rest service with these two parameters(enterpriseid,circuitType).
To achieve the above functionality, implemented the Interceptors class (VRestInterceptor) like below:
public class VRestInterceptor implemnets PreProcessInterceptor,AcceptedByMethod {
public boolean accept(Class declaring, Method method) {
for (Annotation[] annotations : method.getParameterAnnotations()) {
for (Annotation annotation : annotations) {
if(annotation.annotationType() == VRestAuto.class){
VRestAuto vRestAuto = (VRestAuto) annotation;
return vRestAuto.value().equals("EnterpriseId");
}
}
}
return false;
}
Override
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
throws Failure, WebApplicationException { ......}
}
I was able to verify the VRestAuto annotation in the accept method. But in the preProcess Method, how can I call the REST method with two parameters(enterpriseid, circuitType)?
if these interceptors are not suits, Are there any other interceptors best to this functionality?
Your help is highly appreciated .
Why not forget setting the enterpriseId value when the method is called and instead just inject the HttpServletRequest and use that to grab the session and value?
#POST
#Produces("text/json")
#Path("/qciimplinv")
public String getInvSummary(String circuitType, #Context HttpServletRequest servletRequest) {
HttpSession session = servletRequest.getSession();
String enterpriseId = session.getAttribute("EnterpriseId").toString();
....
businessMethod(enterpriseId,circuitType);
....
}

UserNamePasswordValidator and Session Management

I'm using WCF custom Validator with HTTPS (.NET 4.5). Validate on success returns Customer object which I would like to use later. Currently I'm able to do it with Static variables which I like to avoid if possible. I tried to use HttpContext which becomes null in main thread. My understanding Validate runs under different thread. Is there any way I could share session info without involving DB or File share. See related threads here and here.
In Authentication.cs
public class CustomValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
//If User Valid then set Customer object
}
}
In Service.cs
public class Service
{
public string SaveData(string XML)
{
//Need Customer object here. Without it cannot save XML.
//HttpContext null here.
}
}
I can suggest you an alternative approach. Assuming that the WCF service is running in ASP.Net compatibility mode and you are saving the customer object to session storage. Create a class such as AppContext
The code would look something like this
public class AppContext {
public Customer CurrentCustomer {
get {
Customer cachedCustomerDetails = HttpContext.Current.Session[CUSTOMERSESSIONKEY] as Customer;
if (cachedCustomerDetails != null)
{
return cachedCustomerDetails;
}
else
{
lock (lockObject)
{
if (HttpContext.Current.Session[CUSTOMERSESSIONKEY] != null) //Thread double entry safeguard
{
return HttpContext.Current.Session[CUSTOMERSESSIONKEY] as Customer;
}
Customer CustomerDetails = ;//Load customer details based on Logged in user using HttpContext.Current.User.Identity.Name
if (CustomerDetails != null)
{
HttpContext.Current.Session[CUSTOMERSESSIONKEY] = CustomerDetails;
}
return CustomerDetails;
}
}
}
}
The basic idea here is to do lazy loading of data, when both WCF and ASP.Net pipelines have executed and HTTPContext is available.
Hope it helps.
Alright this should have been easier. Since the way UserNamePasswordValidator works, I needed to use custom Authorization to pass UserName/Password to the main thread and get customer info again from the database. This is an additional DB call but acceptable workaround for now. Please download code from Rory Primrose's genius blog entry.

client object model - disable event firing

Is there a way to disable event firing when doing a list item update from the Managed Client Object Model?
In the server model, I do the below. However, I cannot find a way of doing the same on the Managed Client ObjectModel:
class DisabledEventsScope : SPItemEventReceiver, IDisposable
{ // Boolean to hold the original value of the EventFiringEnabled property
bool _originalValue;
public DisabledEventsScope()
{
// Save off the original value of EventFiringEnabled
_originalValue = base.EventFiringEnabled;
// Set EventFiringEnabled to false to disable it
base.EventFiringEnabled = false;
}
public void Dispose()
{
// Set EventFiringEnabled back to its original value
base.EventFiringEnabled = _originalValue;
}
}
using (DisabledEventsScope scope = new DisabledEventsScope())
{
// State-changing operation occurs here.
spItem.Update();
}
Thanks in advance.
You can not do it in client object model, see the MSDN documentation on the SP.List object: http://msdn.microsoft.com/en-us/library/ee554951. But you can develop custom web service which will be called from a client side and disable events' firing.