Long time execution action asp.net core mvc - asp.net-core

What is the best approach to write an action that takes 1 minute or more to execute but returns immediately the control to the view ?
I know that there is Hangfire solution, but it uses a database to store executions and I need something more simple.
I´m using asp.net core.
This is what my action is doing:
private void CipherData()
{
var posts = _context.Posts.All().ToList();
foreach (var post in posts)
{
post.TextCrypt = EncryptionHelper.Encrypt(post.Text);
_context.Posts.Update(post);
}
_context.SaveChanges();
}

I was having problem using Task.Run (that is a way to do this), because it loses the database context (injected).
So I create a new database context to use in this process. Basically:
public IActionResult CipherData()
{
Task.Run(() =>
{
//create a new dbcontext here
//perform database manipulation using this new dbcontext
}
return RedirectToAction("Index", "Home");
}

Related

How to wait for asp.net core configuration to be done for backgroundworkers?

I created a blazor server application which also makes use of BackgroundService / IHostedService - Unfortunately those services Start+Execute before the Configure method within Startup is called. Therefore migrations/database creation are not done before they are used in the services.
I already found a workaround for this, but i am wondering if there is a proper way to solve this.
My workaround for those who have the same issue is this:
public class ApplicationStateTransmitter
{
private readonly TaskCompletionSource _configurationDone;
public ApplicationStateTransmitter()
{
_configurationDone = new TaskCompletionSource(null);
}
public Task ConfigurationDone => _configurationDone.Task;
public void NotifyConfigurationDone() => _configurationDone.SetResult();
}
Startup.ConfigureServices:
services.AddSingleton<ApplicationStateTransmitter>();
Startup.Configure:
using (var serviceScope = serviceScopeFactory.CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
if (!context.Database.CanConnect())
{
logger.LogInformation("Database does not exist yet - Creating database through migrations");
context.Database.Migrate();
}
else
{
if (context.Database.GetPendingMigrations().Any())
{
logger.LogInformation("Applying database migration");
context.Database.Migrate();
logger.LogInformation("Applying database migration done");
}
else
{
logger.LogDebug("No pending migrations found");
}
}
}
}
applicationStateTransmitter.NotifyConfigurationDone();
logger.LogInformation("Configuration done");
Inject instance and await like this
await _applicationStateTransmitter.ConfigurationDone;

What is the equivalent of Web Forms "Page_Load" in ASP.NET Core 5, so my code will run before any page loading?

Is there a way to execute a code on every page load in ASP.NET Core 5, like there is in Web Forms? In Web Forms I used the Page_Load() event handler, but what is the equivalent in ASP.NET Core 5? So if I call any action in any controller, it will run my code first, then run the action execution.
I found this: How can I execute common code for every request?, But when I tried it I got errors.
Please someone provide me with clear solution.
Also, I need a solution to check the session from one place, instead of writing the "check session code" in each controller, I create the session after the login is succeed, but what is the best way to check the session in all controllers and if it is null then redirect to login page?
In asp.net core, you can use Action filters to replace the "Page_Load" method in
webform.
You can register the global scope of Action filters in the startup to ensure that the Action filters will be executed before each action is executed.
Add the following Action filter in your project :
public class MyActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
if (!context.HttpContext.Request.Path.ToString().Contains("Login"))
{
if (context.HttpContext.Session.GetString("user") == null)
{
context.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "Login" }, { "action", "Index" } });
}
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
}
}
Then in startup.cs ConfigureServices method, add following code to apply it to all scope:
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(MyActionFilter));
});

.NET Core Identity API with permission based auth

I'm new at Identity API but in my web application: Institution users creates other users for own institution and and they want to decide who see this page or not.My controller methods like this ;
[Authorize]
public IActionResult Privacy()
{
return View();
}
But also user's have permissions to do any actions like this enum and enum is bigger than 50;
public enum PermissionTypes
{
UserCreate = 1,
UserEdit = 2,
UserDelete = 3,
....
}
And i do some research and found policy based authorization but when you create a new policy you must declare at Startup.cs and its not good for me because when you do that you always publish new codes in production.What i need is something like that ;
[CustomAuth(PermissionTypes.UserCreate)]
public IActionResult Privacy()
{
return View();
}
Is there any solution for this situation ?
There is many ways to do this. A lot of people recommend claims and policy based security... I personally found this approach a little "stiff".
So instead I do this a little different:
First create a class like this:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Identity;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Bamboo.Web.CoreWebsite.Membership
{
public class PermissionHandler : AuthorizationHandler<RolesAuthorizationRequirement>
{
private readonly IUserStore<CustomUser> _userStore;
public PermissionHandler(IUserStore<CustomeUser> userStore)
{
_userStore = userStore;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
{
if(context == null || context.User == null)
return;
var userId = context.User.FindFirst(c => string.CompareOrdinal(c.Type, ClaimTypes.NameIdentifier) == 0);//according to msdn this method returns null if not found
if(userId == null)
return;
// for simplicity, I use only one role at a time in the attribute
//but you can use multiple values
var permissions = requirement.AllowedRoles.ToList();
var hasPermissions = //here is your logic to check the database for the actual permissions for this user.
// hasPermissions is just a boolean which is the result of your logic....
if(hasPermissions)
context.Succeed(requirement);//the user met your custom criteria
else
context.Fail();//the user lacks permissions.
}
}
}
Now inject the PermissionHandler in your startup.cs file like this:
public void ConfigureServices(IServiceCollection services)
{
// Custom Identity Services
........
// custom role checks, to check the roles in DB
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
//the rest of your injection logic omitted for brevity.......
}
Now use it in your actions like this:
[Authorize(Roles = PermissionTypes.UserCreate)]
public IActionResult Privacy()
{
return View();
}
Notice I did not create a custom attribute... Like I said there is many ways to do this.
I prefer this way because is less code and there is no hard-coded policies or claims or any other complexities and you can make it 100% data driven.
This is a complex subject so there might be extra tweaks necessary for it work.
Also I use ASP.NET Core 2.2 which might be different than 3.0.
But it should give you a way to do permission based Authorization.
You need to use Roles within your action.
ASP .NET Core Identity Roles

Insertion failed when used Asp.Net Mvc, Linq, Entity Framework

I am trying to add row to table using asp.net mvc kendo ui. But for the first time, it is a success. when do second time, the debugging pointer says "The process or thread has been changed since the last step" and try to add the first insertion values also. Since the table does not allow the duplication of primary key the insertion fails at second time. Please advie.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Insert([DataSourceRequest] DataSourceRequest request, AdminHeaderImage batchModel)
{
if (ModelState.IsValid)
{
using (var Entity = new DealerEntities())
{
Entity.AdminHeaderImages.AddObject(batchModel);
Entity.SaveChanges();
return RedirectToAction("Index");
}
}
else
{
return RedirectToAction("Index");
}
}
public ActionResult Index()
{
using (var Entity = new DealerEntities())
{
var outPut = Entity.AdminHeaderImages.ToList();
return View(outPut);
}
}
It seems your Entity object is shared between requests.
I recommend having an entity object per web request.
This article explains more
http://blogs.microsoft.co.il/blogs/gilf/archive/2010/05/18/how-to-manage-objectcontext-per-request-in-asp-net.aspx
I just used the following code at the end of Insert which would refresh my entity.
"return Json(new [] { product }.ToDataSourceResult(request, ModelState));"
This solved my problem.

Preserving model in ASP.NET MVC 4

I have an ASP.NET MVC 4 app. I'm relatively new to ASP.NET MVC 4. Currently, I'm trying to build a basic Task list app.
public ActionResult Create()
{
var model = new TaskModel();
return View("~/Views/Task.cshtml", model);
}
[HttpPost]
public ActionResult Create(TaskModel model)
{
if (model.TaskName.Length == 0)
{
// Display error message
}
else
{
// Save to database
// Write success message
}
return View("~/Views/Task.cshtml", model);
}
If there is an error, I display an error message on the screen. My problem is, the previously entered values in the view are not shown. The entire view is blank.How do I preserve the values in the view in this case?
Thank you!
I use TempData for this.
public ActionResult Create()
{
var model = new TaskModel();
TempData["task"] = model;
return View("~/Views/Task.cshtml", model);
}
[HttpPost]
public ActionResult Create()
{
var task = (TaskModel)TempData["task"];
UpdateModel(task);
if (model.TaskName.Length == 0)
{
// Display error message
}
else
{
// Save to database
// Write success message
}
TempData["task"] = task;
return View("~/Views/Task.cshtml", model);
}
MVC works different than WebForms, since there is no concept of 'controls', you have to preserve the state yourself. Another option if you don't want to use TempData is to use an LosFormatter to Serialize your controls into a hidden HTML field. This would replicate the functionality of ViewState in ASP.NET WebForms