can not set properties with Session.CustomActionData - properties

Can not set a property value using Session.CustomActionData.
if (s.CustomActionData.ContainsKey(PropertyName0))
s.CustomActionData[PropertyName0] = "1";
else
s.CustomActionData.Add(PropertyName0, "1");
Although this code works, when the custom action ends, the properties remain unchanged.
So how do I set the property value?
I need to set the property value in one custom action and read it in another.

It is not clear what you are doing.
Only few properties are available in the deferred custom action. Are you trying to pass data to a deferred custom action?
You can write an immediate custom action to write properties and then pass these to a deferred custom action if you need this at run time.
Here is an example of an immediate CA
[CustomAction]
public static ActionResult ImmediateCA(Session session)
{
CustomActionData data = new CustomActionData();
data["property1"] = "abc";
data["property2"] = "def";
session["myDeferredCAData"] = data.ToString();
return ActionResult.Success;
}
[CustomAction]
public static ActionResult myDeferredCA(Session session)
{
CustomActionData data = session.CustomActionData;
string property1 = data["property1"];
return ActionResult.Success;
}
This solution is proposed by Nick Ramirez

Are you using managed code for this? I know that I've had trouble with getting C++ CAs to take in a property and update the property in one CA. In the past I've written to the registry in one CA and read from it into a property in another to get around this problem.

It is impossible to set a property in a deferred custom action.
http://msdn.microsoft.com/en-us/library/aa370543(v=VS.85).aspx

Related

Set value configuration.GetSection("").Value from header request

I need to set in my asp.net core configuration a value from the header in every request.
I'm doing like so:
public async Task Invoke(HttpContext context)
{
var companyId = context.Request.Headers["companyid"].ToString().ToUpper();
configuration.GetSection("CompanyId").Value = companyId;
await next(context);
}
It works fine. But is this the proper way? In case of multiple request at same time is there a risk of messing the values? I've searched around but couldn't find an answer.
I'm using .Net 3.1.
As far as I know, the appsetting.json value is a global setting value, you shouldn't be modifying global state per request, this action is not thread safe. At some point, you will face a rice condition.
If you still want to use this codes, I suggest you could try to add a lock. Notice: This will make your Invoke method very slowly.
Details, you could refer to below codes:
private static Object _factLock = new Object();
lock (_factLock)
{
Configuration.GetSection("CompanyId").Value = "";
}

Modify view path from MVC controller before rendering it

In my MVC 4 Controller, I want to override the View() method
ViewResult View(string viewName, string masterName, object model) {}
So that I can manipulate the view being rendered by the action method. To do this, I want to be able to obtain the physical path of the view file. I have tried the following:
string viewName = this.ControllerContext.RouteData.Route
.GetVirtualPath(this.ControllerContext.RequestContext, null)
.VirtualPath;
For example, this might return "/Errors/MissingParameters" when what I really want it to return is something like:
"~/Views/Errors/MissingParameters"
or, even better:
"~/Views/Errors/MissingParameters.cshtml"
Just to add complication, I also need it to cope with Areas, so if I had the same example running in an Area named "Surveys", I would want it to return something like:
"~/Areas/Surveys/Views/Errors/MissingParameters"
The reason I want to do this is that I'm experimenting with using views for globalization, so I might have two views:
"~/Views/Errors/MissingParameters.cshtml" // default view (en-GB)
"~/Views/Errors/MissingParameters_de-DE.cshtml" // German view (de-DE)
and I want to be able to check if the view exists for the current language/culture before referencing it.
Any advice would be much appreciated.
Thanks.
EDIT: This part will not work or is hard to implement
You'd rather use an action filter which will let you manipulate the Result before executing it.
Particularly you need a Result filter. Implement the IResultFilter.onResultExecuting method, and change the result there. Particularly when you implement this method:
void OnResultExecuting(ResultExecutingContext filterContext)
You can access the ResultExecutingContext.Result Property. This property will contain your view. If you cast it to System.Web.Mvc.ViewResultBase you'll have access to the ViewName and you'll be able to change it.
If you've never implemented a filter, this is a good hands-on-lab on the subject. In this case it implements another kind of filter, but it's just the same.
As an answer to the OP comment, it's perfectly normal that ViewName is missing, and View is still null. ViewName wouldn't be empty only if the case that the view is returned with name, like this: return View("Index");. And, the ViewName would be just, not the whole path to the view. So this is not a solution. So, to have this solution working you would have to deal with route data, controller context, etc. to find the view. (More on this below.)
EDIT: Solution, register a custom view engine
When MVC has to render a view it gets the information from the route data, the controller context, the view name (that, as explained above can be empty), and the conventions that apply.
Particularly, in MVC there is a collection of registered view engines which are required to find the view calling there FindView() method. The view engine will return a ViewEngineResult which has the found view, if one was found, or a list of the paths where the view has been unsuccesfully sought.
So, to modify the template path, you can override this funcionality: let the original class find the view, and, if it is found, modify the path.
To do show you need to take theses steps:
Inherit the view engine which you're using (my sampel code inherits Razor view engine)
Register your vie engine, so that it's queried before the original view engine (in my sample code I simply clear the registered engines list, and register mine. The original list includes razor and web form view engines)
This is the code for the inherited view engine:
public class CustomRazorViewEngine : FixedRazorViewEngine
{
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
ViewEngineResult result
= base.FindView(controllerContext, viewName, masterName, useCache);
if (result.View != null)
{
// Modify here !!
}
return result;
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
ViewEngineResult result
= base.FindPartialView(controllerContext, partialViewName, useCache);
if (result.View != null)
{
// Modify here !!
}
return result;
}
static readonly PropertyInfo ViewPathProp
= typeof(RazorView).GetProperty("ViewPath");
public void SetViewPath(RazorView view, string path)
{
ViewPathProp.SetValue(view, path);
}
}
NOTE 1: where you read // Modify here !! you can modify the path property of the result.View. Cast it to RazorView: (result.View as RazorView).ViewPath. As the ViewPath setter is protected, you need to set it using Reflection: you can use the SetViewPath method for this.
NOTE 2: As you can see I'm not inheriting the RazorViewEngine but the FixedRazorViewEngine. If you loook for this class in MSDN you'll get not results, but if you look the original content of the registered view engines list, you'll find this class. I think this depends on an installed package in the project, and I think it solves a bug in MVC4. If you don't finf it in Microsoft.Web.Mvc namespace, inherit the original RazorViewEngined
NOTE 3: after the view is found, the view engine executes it, using the ViewEngineResult, so, if you change it, it will be executed with the new view path
And finally, you need to change the list of registered engines, in global.asax application start event, like this:
protected void Application_Start()
{
// Original content:
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Added content:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomRazorViewEngine());
}
NOTE: it would be cleaner, if you created a ViewEngineConfig class in App_Start folder, and invoked an static method of this class, just as it's done with all other configurations.
Answer was copied from here.
Well if you don't mind having your code tied to the specific view engine you're using, you can look at the ViewContext.View property and cast it to WebFormView
var viewPath = ((WebFormView)ViewContext.View).ViewPath;
I believe that will get you the view name at the end.
EDIT: Haacked is absolutely spot-on; to make things a bit neater I've wrapped the logic up in an extension method like so:
public static class IViewExtensions {
public static string GetWebFormViewName(this IView view) {
if (view is WebFormView) {
string viewUrl = ((WebFormView)view).ViewPath;
string viewFileName = viewUrl.Substring(viewUrl.LastIndexOf('/'));
string viewFileNameWithoutExtension = Path.GetFileNameWithoutExtension(viewFileName);
return (viewFileNameWithoutExtension);
} else {
throw (new InvalidOperationException("This view is not a WebFormView"));
}
}
}
which seems to do exactly what I was after.
Another solution here
((System.Web.Mvc.RazorView)htmlHelper.ViewContext.View).ViewPath
net-mvc
i am registering the area but i don't want my
url: "{area}/{controller}/{action}/{id}",
instead i want it to be like
url: "{controller}/{action}/{id}",
so i have registered my area like
context.MapRoute(
name: "AreaName_default",
url: "{controller}/{action}/{id}",
namespaces: new[] { "SolutionName.AreaName.Controllers" }
);
and i don't want to add the hard code string viewpath while returning view in every action method like
return View("~/Areas/AreaName/Views/ControllerName/ViewName.cshtml", model);
so i have created one result filter and override OnResultExecuting function
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
string areaName = AreaNameAreaRegistration.PropoertyName;
if (filterContext.Result.GetType() == typeof(ViewResult) || filterContext.Result.GetType() == typeof(PartialViewResult))
{
dynamic viewResult = filterContext.Result;
string viewname = string.IsNullOrEmpty(viewResult.ViewName) ? Convert.ToString(filterContext.RouteData.Values["action"]) : viewResult.ViewName;
string folder = Convert.ToString(filterContext.RouteData.Values["controller"]);
string lateralHireAreaViewPath = $"~/Areas/{areaName}/Views/";
string extension = viewname.Contains(".cshtml") ? "" : ".cshtml";
viewResult.ViewName = string.Concat(lateralHireAreaViewPath, folder, "/", viewname, extension);
ViewEngineResult result = ViewEngines.Engines.FindView(filterContext.Controller.ControllerContext, viewResult.ViewName, null);
if (result.View == null)
{
//searched in shared folder
lateralHireAreaViewPath = string.Concat(lateralHireAreaViewPath, "Shared/");
viewResult.ViewName = string.Concat(lateralHireAreaViewPath, "/", viewname, extension);
}
}
}

How can I update user data form session in ServiceStack?

Very simple question: I have a session object in my Service:
var session = this.GetSession(); //IAuthSession
if (!session.IsAuthenticated)
I can modify some values in the session class (e.g. Permissions) based on the parameters passed to the service; then I want to save them.
How?
The direct way of doing it: create a UserAuth object, popolate it with all the fields from IAuthSession, get the IDbConnectionFactory, save it.
Surely there is a faster and better way, but I was not able to find it!
More generally, how can I switch between IAuthSession anf UserAuth? I.e., given a IAuthSession object, how can I obtain a UserAuth object, modify it, and persist the modifications?
I have read this question on how to append metadata to a user login info, but something is still missing.
Once you have added what you need, how do you save it? (I doubt you just add the metadata to both session and UserAuth, and then you use IDbConnectionFactory to save the latter; there must be a better way!)
Old question but worth answering.
The UserAuthRepository being used should have an implementation of the UpdateUserAuth method that can be called to save the UserAuth changes
UpdateUserAuth(UserAuth existingUser, UserAuth newUser, string password)
Another easier way would be to just call the RegisterService using PUT which will update the existing registered user for you.
/// <summary>
/// Update an existing registraiton
/// </summary>
public object Put(Register request)
{
return Post(request);
}
The service call would be something similar to this:
using (var authService = base.ResolveService<RegisterService>())
{
var authResponse = authService.Put(
new Register {
UserName = session.UserName ?? session.Email,
Email = session.Email,
etc...
});
if (authResponse is IHttpError)
throw (Exception)authResponse;
}

Model binding validation errors

In my custom model validation, I have the following:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext){
var repository = DependencyResolver.Current.GetService(typeof(IContactRepository));
IContactRepository repo = repository as IContactRepository;
USRContact c = repo.GetContactByID(Convert.ToInt64(bindingContext.ValueProvider.GetValue("ContactID").AttemptedValue));
c.FormalName = bindingContext.ValueProvider.GetValue("FormalName").AttemptedValue;
if (!repo.IsValidFormalName(c.ContactID, c.FormalName))
{
var result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
bindingContext.ModelState.AddModelError("FormalName", Resources.ErrorMsgs.FormalNameNotUnique);
return bindingContext.Model;
}
c.PreferredName = bindingContext.ValueProvider.GetValue("PreferredName").AttemptedValue;
c.Alias = bindingContext.ValueProvider.GetValue("Alias").AttemptedValue;
c.Pseudonym = bindingContext.ValueProvider.GetValue("Pseudonym").AttemptedValue;
c.GenderID = Convert.ToInt32(bindingContext.ValueProvider.GetValue("GenderID").AttemptedValue);
c.NationalityID = Convert.ToInt32(bindingContext.ValueProvider.GetValue("NationalityID").AttemptedValue);
c.ModifiedByID = Utilities.SessionUtil.Current.UserID;
c.ModifiedDate = DateTime.Now;
}
My controller calls this model binder by doing the following:
public ActionResult Update([ModelBinder(typeof(ModelBinder.ContactModelBinder))] USR.USRContact contact)
{
if (ModelState.IsValid)
{
repository.Update();
return View("~/Views/Shared/Contacts/ShowContactInfo.cshtml", repository.GetContactByID(contact.ContactID));
}
}
}
My viewmodel contains the data annotations to say that formal name is required and that alias needs to be less than 60 characters. How do I display the errors if the model binder transformed it to the persistent data model(USRContact) and my view is expecting the viewmodel?
Is there any way to make sure that on validation errors on the view model, the controller doesn't transform to persistent data model? Even if we do check for all the model errors in the data object and find the validation errors, how do we send the user back to the view they were just in with the errors next to the textboxes that they had the error in.
Thanks for the help!
Safris
I think the issue you may be facing is that once you push those values into the other object through the custom binder they are no longer the same as they were on the page.
A property called "PropertyValue" with an Html.ValidationFor(x=>x.PropertyValue) is going to look in the ModelState error collection for items with PropertyValue.
Once you have pushed those into Contact now the value is Contact.PropertyValue. If you validated it then it will be added to the ModelState as "Contact.PropertyValue" This will only be picked up by Html.ValidationFor(x=>x.Contact.PropertyValue)
The easiest solution is to make sure that your input and output's follow the same structure. If you can render the items as Html.TextBoxFor(x=>x.Contact.SomeProperty) then things will be fine.

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.