I want to know how can I access virtual actionresult, since I haven't seen so far this type of actionresults. I've read little bit and I can see that is related to t4mvc but I'm not sure how can I access it.
This is what I have, ScheduleController, which has this actionresult for example:
[Authorize]
public virtual ActionResult AdminSport(int sportId)
{
Sport sport = _rep.GetSport(sportId);
if(sport == null)
{
return this.ViewNotFound();
}
if(!User.IsScheduleAdmin(sport))
{
return this.ViewNotAuthorized();
}
var ret = _rep.ListScheduleEntriesForAD(sport);
return View(ret);
}
Then, the only other instance of that controller I found was in T4MVC, and this is whats in that file:
[NonAction]
partial void AdminSportOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, int sportId);
[NonAction]
public override System.Web.Mvc.ActionResult AdminSport(int sportId)
{
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.AdminSport);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "sportId", sportId);
AdminSportOverride(callInfo, sportId);
return callInfo;
}
My question is how can I access that specific action, for example if I type in the browser: /Schedule/AdminSport I'm getting an error.
Any idea what/how do I need to fix this?
Related
We have an ASP.NET application. We cannot edit source code of controllers. But we can implement ActionFilter.
One of our controller action methods returns JSON. Is it possible to modify it in ActionFilter? We need to add one more property to a returned object.
Maybe, some other way to achieve it?
Found this interesting and as #Chris mentioned, though conceptually I knew this would work, I never tried this and hence thought of giving it a shot. I'm not sure whether this is an elegant/correct way of doing it, but this worked for me. (I'm trying to add Age property dynamically using ActionResult)
[PropertyInjector("Age", 12)]
public ActionResult Index()
{
return Json(new { Name = "Hello World" }, JsonRequestBehavior.AllowGet);
}
And the filter:
public class PropertyInjector : ActionFilterAttribute
{
string key;
object value;
public PropertyInjector(string key, object value)
{
this.key = key;
this.value = value;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var jsonData = ((JsonResult)filterContext.Result).Data;
JObject data = JObject.FromObject(jsonData);
data.Add(this.key,JToken.FromObject(this.value));
filterContext.Result = new ContentResult { Content = data.ToString(), ContentType = "application/json" };
base.OnActionExecuted(filterContext);
}
}
Update
If it's not dynamic data which is to be injected, then remove filter constructor and hard code key & value directly and then the filter could be registered globally without editing the controller
GlobalFilters.Filters.Add(new PropertyInjector());
I am asking this because after long time searching I haven't found a good answer on this yet...
Here is what I want:
Example: I have a domain model "JobPosting" which a user should be able to change state to published, if it is still a draft. Before publishing I must not only validate the model properties I must also validate many different requirements regarding the user account, it's registered company etc. All this validation logic is put into a service layer. So far so good...
This is how my service layer looks like:
public IValidationResult ValidatePublish(JobPosting jobPosting){
...
}
public void Publish(JobPosting jobPosting){
jobPosting.State = JobPostingState.Published;
...
}
Any my controller:
public ActionResult Publish(PublishViewModel model){
...
var validationResult = _jobService.ValidatePublish(jobPosting);
if(validationResult.Success){
_jobService.Publish(jobPosting);
...
}
...
}
And here now my questions:
I want to be able to call the ValidatePublish from the controller to show validation errors in the view. However I must never be able to publish a job when validation fails.
So to have my code more robust I added a second validation check in my Publish method in service layer:
public void Publish(JobPosting jobPosting){
if(ValidatePublish(jobPosting).Success){
jobPosting.State = JobPostingState.Published;
...
}
}
but I have not such a good feeling with this approach because now I am calling the validation twice when validation is OK during each controller publish request.
What do you think. Is the second call to much? Is there a better approach?
I am asking because my whole application looks like that and if I would ever forget a validation call in controller I might end up with an not allowed domain model state in database. That's why I added the second validation check in each service method.
Thanks in advance for your thoughts on this!!!
One quick solution might be to have the Publisher class require the JobPosting and IValidationResult objects as arguments.
public void Publish(JobPosting jobPosting, IValidationResult validation)
{
if (validation.IsValid)
{
jobPosting.State = JobPostingState.Published;
// other work here...
}
}
Your Controller can then call the Validator, receive an IValidationResult and pass that back to the presentation layer if needed. Otherwise pass on to Publisher
public ActionResult Publish(PublishViewModel model)
{
var validationResult = _jobService.ValidatePublish(jobPosting);
if(validationResult.Success) _jobService.Publish(jobPosting, validationResult);
else return View("error", validationResult);
}
Edit:
A cleaner solution may be to have the Publisher class return a PublishAttempt result.
public class PublishAttempt : IValidationResult
{
public enum AttemptOutcome {get; set;}
}
public ActionResult Publish(PublishViewModel model)
{
var attempt = _jobService.Publish(jobPosting);
if (attempt.Success) return View("success");
else return View("error", attempt.ValidationResults);
}
The following just came into my mind... what do you think:
I change my service method to:
public IValidationResult Publish(JobPosting jobPosting, bool validateOnly = false){
var validationResult = ValidatePublish(jobPosting);
if(validateOnly) return validationResult;
jobPosting.State = JobPostingState.Published;
...
return validationResult;
}
And then in controller I always call only the Publish method and not the extra ValidatePublish anymore:
public ActionResult Publish(PublishViewModel model)
{
var validationResult = _jobService.Publish(jobPosting);
if(!validationResult.Success) return View("error", validationResult);
}
And when I need only simple validation I do
var validationResult = _jobService.Publish(jobPosting, true);
Is this okey to do it like that?
Or is it not good looking if a normal service call returns IValidationResult?
I have a following problem. I register my components and initialize them in Unity like this (example is for a Console application):
public class SharePointBootstrapper : UnityBootstrapper
{
...
public object Initialize(Type type, object parameter) =>
Container.Resolve(type,
new DependencyOverride<IClientContext>(Container.Resolve<IClientContext>(parameter.ToString())),
new DependencyOverride<ITenantRepository>(Container.Resolve<ITenantRepository>(parameter.ToString())));
public void RegisterComponents()
{
Container
.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString())
.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString())
.RegisterType<ITenantRepository, DocumentDbTenantRepository>(SharePointClientContext.Online.ToString())
.RegisterType<ITenantRepository, JsonTenantRepository>(SharePointClientContext.OnPremise.ToString());
}
}
public enum SharePointClientContext
{
Online,
OnPremise
}
class Program
{
static void Main(string[] args)
{
...
bootstrap.RegisterComponents();
var bla = bootstrap.Initialize(typeof(ISharePointManager), SharePointClientContext.Online);
}
}
So, I register my components in MVC, WCF, Console etc. once with RegisterComponents() and initialize them with Initialize().
My question is, if I want to initialize specific named registration at runtime, from e.g. user input, can it be done otherwise as the code presented (with InjectionFactory or similar)?
This code works fine, but I'm not happy with its implementation. I have a feeling that it could be written in RegisterComponents() instead of Initialize() so that it accepts a parameter of some type, but I don't know how to do it.
Or, is maybe my whole concept wrong? If so, what would you suggest? I need to resolve named registration from a parameter that is only known at runtime, regardless of the technology (MVC, WCF, Console, ...).
Thanks!
Instead of doing different registrations, I would do different resolves.
Let's say that you need to inject IClientContext, but you want different implementations depending on a runtime parameter.
I wrote a similiar answer here. Instead of injecting IClientContext, you could inject IClientContextFactory, which would be responsible for returning the correct IClientContext. It's called Strategy Pattern.
public interface IClientContextFactory
{
string Context { get; } // Add context to the interface.
}
public class SharePointOnlineClientContext : IClientContextFactory
{
public string Context
{
get
{
return SharePointClientContext.Online.ToString();
}
}
}
// Factory for resolving IClientContext.
public class ClientContextFactory : IClientContextFactory
{
public IEnumerable<IClientContext> _clientContexts;
public Factory(IClientContext[] clientContexts)
{
_clientContexts = clientContexts;
}
public IClientContext GetClientContext(string parameter)
{
IClientContext clientContext = _clientContexts.FirstOrDefault(x => x.Context == parameter);
return clientContext;
}
}
Register them all, just as you did. But instead of injecting IClientContext you inject IClientContextFactor.
There also another solution where you use a Func-factory. Look at option 3, in this answer. One may argue that this is a wrapper for the service locator-pattern, but I'll leave that discussion for another time.
public class ClientContextFactory : IClientContextFactory
{
private readonly Func<string, IClientContext> _createFunc;
public Factory(Func<string, IClientContext> createFunc)
{
_createFunc = createFunc;
}
public IClientContext CreateClientContext(string writesTo)
{
return _createFunc(writesTo);
}
}
And use named registrations:
container.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString());
container.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString());
container.RegisterType<IFactory, Factory>(
new ContainerControlledLifetimeManager(), // Or any other lifetimemanager.
new InjectionConstructor(
new Func<string, IClientContext>(
context => container.Resolve<IClientContext>(context));
Usage:
public class MyService
{
public MyService(IClientContextFactory clientContextFactory)
{
_clientContextFactory = clientContextFactory;
}
public void DoStuff();
{
var myContext = SharePointClientContext.Online.ToString();
IClientContextclientContext = _clientContextFactory.CreateClientContext(myContext);
}
}
I have close to 10 controllers that currently share the same code. The code is pretty simple, it just checks if a set of data is null and checks if the current user has permission to access the data.
If there is an issue, I throw an HttpResponseException.
The code works when it is sitting in each controller. I have also managed to centralize the code but I think the way I have done it is wrong. I've created a new class which inherits ApiController and then I have the controllers inheriting my new class. This is the only way I could get the HttpResponseExceptions working. Code is as follows:
//New centralized class:
public class AuthorizationClass : ApiController
{
private DataModel db = new DataModel();
public async Task checkUserisValid(int user_id)
{
user_list user_list = await db.user_list.FindAsync(user_id);
if (user_list == null)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest,"This user does not exist"));
}
int businessID = user_list.business_id;
var result = checkAccess(User.Identity.Name, businessID);
if (result.Count <= 0)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You do not have access to modify this business"));
}
}
public static List<user_details> checkAccess(string userName, int id)
{
//code which checks if the user is in the right tables
return checkAccess.ToList();
}
}
Then in the controller class, I have:
public class MyController : AuthorizationClass
{
public async Task<IHttpActionResult> Postnew_table(int id, new_table new_table)
{
await checkUserisValid(id);
//rest of controller
}
}
I tried to do it in different ways but this is the only way I could get it working with HttpResponseException. Is there a better way to do this without inheriting classes or is this the only way to do what I am after?
Thanks.
You could just move these 2 methods to some static helper class in a common assembly, you mention that Request is an instance variable on the controller, just pass it to the method.
public static class SomeHelper
{
public static async Task checkUserisValid(int user_id, DataModel db, Request request, User user)
{
user_list user_list = await db.user_list.FindAsync(user_id);
if (user_list == null)
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest,"This user does not exist"));
}
int businessID = user_list.business_id;
var result = checkAccess(user.Identity.Name, businessID);
if (result.Count <= 0)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You do not have access to modify this business"));
}
}
public static List<user_details> checkAccess(string userName, int id)
{
//code which checks if the user is in the right tables
return checkAccess.ToList();
}
}
Can someone please check out this code, i really dont understand why i got violation of unique when i try to update an record. the code used to create new record work just fine, but when i try to use it to update, it called out violation.
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(User user)
{
if (ModelState.IsValid)
{
userRepository.SaveUser(user);
return RedirectToAction("List");
}
else
return View("Edit");
}
userRepo:
public void SaveUser(User user)
{
user.LAST_ACTIVITY = DateTime.Now;
if (user.USER_ID != 0)
{
usersTable.Attach(user);
usersTable.Context.Refresh(RefreshMode.KeepCurrentValues, user);
}
else
{
usersTable.InsertOnSubmit(user);
}
usersTable.Context.SubmitChanges();
}
and i got an error:
Unable to refresh the specified
object. The object no longer exists
in the database.
when i try to change the userRepo like this for testing purpose.
public void SaveUser(User user)
{
user.LAST_ACTIVITY = DateTime.Now;
usersTable.Attach(user);
usersTable.Context.Refresh(RefreshMode.KeepCurrentValues, user);
usersTable.Context.SubmitChanges();
}
Im wondering if there anyone on this board can find out if i am wrong somewhere in this problem :).
Thank you very much and wish you best regard. :)
Looks like you have conflicts to resolve even though you're telling it to "KeepCurrentValues", try this before the submit changes...
foreach(ObjectChangeConflict conflict in usersTable.Context.ChangeConflicts)
{
foreach(MemberChangeConflict memberConflict in conflict.MemberConflicts)
{
memberConflict.Resolve(RefreshMode.KeepCurrentValues);
}
}
Ignore ID on your model binding.
[Bind(Exclude= "Id")]
public ActionResult Edit(User user)
Kindness,
Dan