i want to clear all textboxes. wrote the public function as:
public void clean(Control parent)
{
try
{
foreach (Control c in parent.Controls)
{
TextBox tb = c as TextBox; //if the control is a textbox
if (tb != null)//Will be null if c is not a TextBox
{
tb.Text = String.Empty;//display nothing
}
}
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
}
}
in the class of the page i want it to be called i declared:
PublicFunctions pubvar = new PublicFunctions();
and i call it as
pubvar.clean(Page);
but its not working... not even throwing an error... and my textboxes arent clearing... help?
You should use recursive loop to check all the controls.
try this code
using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
public class PublicFunctions
{
public void Clean(Control parent)
{
var controls = GetAllControls(parent);
foreach (Control c in controls)
{
TextBox tb = c as TextBox;
if (tb != null)
{
tb.Text = String.Empty;
}
}
}
public IEnumerable<Control> GetAllControls(Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control innerControl in control.Controls)
{
yield return innerControl;
}
}
}
}
Related
I got an MVC 5 application that i'm porting to asp.net Core.
In the MVC application call to controller we're made using AngularJS $resource (sending JSON) and we we're POSTing data doing :
ressource.save({ entries: vm.entries, projectId: vm.project.id }).$promise...
that will send a JSON body like:
{
entries:
[
{
// lots of fields
}
],
projectId:12
}
the MVC controller looked like this :
[HttpPost]
public JsonResult Save(List<EntryViewModel> entries, int projectId) {
// code here
}
How can I replicate the same behaviour with .NET Core since we can't have multiple [FromBody]
you cannot have multiple parameter with the FromBody attibute in an action method. If that is need, use a complex type such as a class with properties equivalent to the parameter or dynamic type like that
[HttpPost("save/{projectId}")]
public JsonResult Save(int projectId, [FromBody] dynamic entries) {
// code here
}
As pointed out in the comment, one possible solution is to unify the properties you're posting onto a single model class.
Something like the following should do the trick:
public class SaveModel
{
public List<EntryViewModel> Entries{get;set;}
public int ProjectId {get;set;}
}
Don't forget to decorate the model with the [FromBody] attribute:
[HttpPost]
public JsonResult Save([FromBody]SaveViewModel model)
{
// code here
}
Hope this helps!
It's still rough but I made a Filter to mimic the feature.
public class OldMVCFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Method != "GET")
{
var body = context.HttpContext.Request.Body;
JToken token = null;
var param = context.ActionDescriptor.Parameters;
using (var reader = new StreamReader(body))
using (var jsonReader = new JsonTextReader(reader))
{
jsonReader.CloseInput = false;
token = JToken.Load(jsonReader);
}
if (token != null)
{
var serializer = new JsonSerializer();
serializer.DefaultValueHandling = DefaultValueHandling.Populate;
serializer.FloatFormatHandling = FloatFormatHandling.DefaultValue;
foreach (var item in param)
{
JToken model = token[item.Name];
if (model == null)
{
// try to cast the full body as the current object
model = token.Root;
}
if (model != null)
{
model = this.RemoveEmptyChildren(model, item.ParameterType);
var res = model.ToObject(item.ParameterType, serializer);
context.ActionArguments[item.Name] = res;
}
}
}
}
}
private JToken RemoveEmptyChildren(JToken token, Type type)
{
var HasBaseType = type.GenericTypeArguments.Count() > 0;
List<PropertyInfo> PIList = new List<PropertyInfo>();
if (HasBaseType)
{
PIList.AddRange(type.GenericTypeArguments.FirstOrDefault().GetProperties().ToList());
}
else
{
PIList.AddRange(type.GetTypeInfo().GetProperties().ToList());
}
if (token != null)
{
if (token.Type == JTokenType.Object)
{
JObject copy = new JObject();
foreach (JProperty jProp in token.Children<JProperty>())
{
var pi = PIList.FirstOrDefault(p => p.Name == jProp.Name);
if (pi != null) // If destination type dont have this property we ignore it
{
JToken child = jProp.Value;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, pi.PropertyType);
}
if (!IsEmpty(child))
{
if (child.Type == JTokenType.Object || child.Type == JTokenType.Array)
{
// nested value has been checked, we add the object
copy.Add(jProp.Name, child);
}
else
{
if (!pi.Name.ToLowerInvariant().Contains("string"))
{
// ignore empty value when type is not string
var Val = (string)child;
if (!string.IsNullOrWhiteSpace(Val))
{
// we add the property only if it contain meningfull data
copy.Add(jProp.Name, child);
}
}
}
}
}
}
return copy;
}
else if (token.Type == JTokenType.Array)
{
JArray copy = new JArray();
foreach (JToken item in token.Children())
{
JToken child = item;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, type);
}
if (!IsEmpty(child))
{
copy.Add(child);
}
}
return copy;
}
return token;
}
return null;
}
private bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined);
}
}
Suppose I have users with tags being lazy loaded:
public class User
{
public int UserId { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
If I get a user this way, I know that the tags aren't loaded:
User myUser;
using (var context = new MyContext())
{
myUser = context.Users.Find(4);
}
How do I test the Tags collection presence outside of the using clause?
if (myUser.Tags == null) // throws an ObjectDisposedException
I could use a try/catch but there must be a better way.
The only way I can think of is to be able to do a non virtual call (similar to when you do base.Something in a derived class) to the class property getter. Since there is no way to do that with pure C# or reflection, I've ended up with the following helper method which utilizes System.Reflection.Emit LCG (lightweight code generation) to emit Call IL instruction instead of the normal Callvirt:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
public static class Utils
{
public static TValue GetClassValue<TSource, TValue>(this TSource source, Expression<Func<TSource, TValue>> selector)
where TSource : class
{
Func<TSource, TValue> getValue = null;
if (source.GetType() != typeof(TSource))
{
var propertyAccessor = selector.Body as MemberExpression;
if (propertyAccessor != null)
{
var propertyInfo = propertyAccessor.Member as PropertyInfo;
if (propertyInfo != null)
{
var getMethod = propertyInfo.GetGetMethod();
if (getMethod != null && getMethod.IsVirtual)
{
var dynamicMethod = new DynamicMethod("", typeof(TValue), new[] { typeof(TSource) }, typeof(Utils), true);
var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, getMethod, null);
il.Emit(OpCodes.Ret);
getValue = (Func<TSource, TValue>)dynamicMethod.CreateDelegate(typeof(Func<TSource, TValue>));
}
}
}
}
if (getValue == null)
getValue = selector.Compile();
return getValue(source);
}
}
It can be used for both single and collection type navigation properties like this:
if (myUser.GetClassValue(x => x.Tags) == null)
Another solution using a naive try/catch, not sure about the performance compared to the other answer:
using System;
public static class EntityFrameworkExtensions
{
public static bool IsCollectionLoaded<TSource, TValue>(this TSource source, Func<TSource, TValue> selector)
where TSource : class
{
try
{
return (selector(source) != null);
}
catch (ObjectDisposedException)
{
return false;
}
}
}
usage:
if (myUser.IsCollectionLoaded(x => x.Tags))
I wish to make two ListBoxes scroll together.
I have two ListBoxes of the same height with the same number of items, etc. I want to set it up such that if the user scrolls up/down in one list box the scrollbar for the other ListBox scrolls up/down as well.
But I can not seem to find a way to either detect the scroll bar position value or to detect when it has changed value.
Here is another way to sync the two ListBoxes:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SyncTwoListBox
{
public partial class Form1 : Form
{
private SyncListBoxes _SyncListBoxes = null;
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
//add options
for (int i = 0; i < 40; i++)
{
listBox1.Items.Add("Item " + i);
listBox2.Items.Add("Item " + i);
}
}
private void Form1_Load(object sender, EventArgs e)
{
this._SyncListBoxes = new SyncListBoxes(this.listBox1, this.listBox2);
}
}
public class SyncListBoxes
{
private ListBox _LB1 = null;
private ListBox _LB2 = null;
private ListBoxScroll _ListBoxScroll1 = null;
private ListBoxScroll _ListBoxScroll2 = null;
public SyncListBoxes(ListBox LB1, ListBox LB2)
{
if (LB1 != null && LB1.IsHandleCreated && LB2 != null && LB2.IsHandleCreated &&
LB1.Items.Count == LB2.Items.Count && LB1.Height == LB2.Height)
{
this._LB1 = LB1;
this._ListBoxScroll1 = new ListBoxScroll(LB1);
this._ListBoxScroll1.Scroll += _ListBoxScroll1_VerticalScroll;
this._LB2 = LB2;
this._ListBoxScroll2 = new ListBoxScroll(LB2);
this._ListBoxScroll2.Scroll += _ListBoxScroll2_VerticalScroll;
this._LB1.SelectedIndexChanged += _LB1_SelectedIndexChanged;
this._LB2.SelectedIndexChanged += _LB2_SelectedIndexChanged;
}
}
private void _LB1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this._LB2.TopIndex != this._LB1.TopIndex)
{
this._LB2.TopIndex = this._LB1.TopIndex;
}
if (this._LB2.SelectedIndex != this._LB1.SelectedIndex)
{
this._LB2.SelectedIndex = this._LB1.SelectedIndex;
}
}
private void _LB2_SelectedIndexChanged(object sender, EventArgs e)
{
if (this._LB1.TopIndex != this._LB2.TopIndex)
{
this._LB1.TopIndex = this._LB2.TopIndex;
}
if (this._LB1.SelectedIndex != this._LB2.SelectedIndex)
{
this._LB1.SelectedIndex = this._LB2.SelectedIndex;
}
}
private void _ListBoxScroll1_VerticalScroll(ListBox LB)
{
if (this._LB2.TopIndex != this._LB1.TopIndex)
{
this._LB2.TopIndex = this._LB1.TopIndex;
}
}
private void _ListBoxScroll2_VerticalScroll(ListBox LB)
{
if (this._LB1.TopIndex != this._LB2.TopIndex)
{
this._LB1.TopIndex = this._LB2.TopIndex;
}
}
private class ListBoxScroll : NativeWindow
{
private ListBox _LB = null;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20a;
public event dlgListBoxScroll Scroll;
public delegate void dlgListBoxScroll(ListBox LB);
public ListBoxScroll(ListBox LB)
{
this._LB = LB;
this.AssignHandle(LB.Handle);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_VSCROLL:
case WM_MOUSEWHEEL:
if (this.Scroll != null)
{
this.Scroll(_LB);
}
break;
}
}
}
}
}
enter image description here
I have a data object called DeliveryPeriod which is a container for a start and a end date (saved as String like dd.MM.yyy, comes from the database) an the id of another object called PlanningPeriod. This delivery period should be displayed in its own custom component in JSF like
<myc:deliveryPeriodComponent value="#{backendBean.deliveryPeriod}" />
I implement a class DeliveryPeriodComponent which extends UIInput and a DeliveryPeriodComponentRenderer which extendes javax.faces.renderer. The rendering works well, i see two calender elements and a SelectOneMenu to choose the planning period. But render the data is not all, I need to change the data as well. And here comes the problem, i have no idea to get the data inside my component to the backend bean. The decode() method did not know the new values and the other methods are never called. I didn't know the trick, how to connect the JSF page to the bean, from the tutorial (http://jsfatwork.irian.at, i bought the book) i had these methods like getValue() and getConverter().
Here is the code from the component:
public class DeliveryPeriodComponent extends UIInput {
public static final String COMPONENT_TYPE = "de.hacon.tps.integrator.web.component.deliveryperiod.DeliveryPeriodComponent";
enum PropertyKeys {
begin, end, planningPeriod
}
public DeliveryPeriodComponent() {
setRendererType("de.hacon.tps.integrator.web.component.deliveryperiod.DeliveryPeriodComponent");
}
public String getBegin() {
return (String) getStateHelper().eval(PropertyKeys.begin, "01.01.2012");
}
public void setBegin(String begin) {
getStateHelper().put(PropertyKeys.begin, begin);
}
public String getEnd() {
return (String) getStateHelper().eval(PropertyKeys.end, "31.12.2012");
}
public void setEnd(String end) {
getStateHelper().put(PropertyKeys.end, end);
}
public int getPlanningPeriod() {
return (Integer) getStateHelper().eval(PropertyKeys.planningPeriod, 0);
}
public void setPlanningPeriod(int planningPeriod) {
getStateHelper().put(PropertyKeys.planningPeriod, planningPeriod);
}}
And here is the renderer:
public class DeliveryPeriodComponentRenderer extends Renderer {
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
#Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
DeliveryPeriodComponent comp = (DeliveryPeriodComponent) component;
String clientId = comp.getId();
try {
encodeInput(context, comp, clientId);
} catch (ParseException e) {
e.printStackTrace();
}
}
private void encodeInput(FacesContext context, DeliveryPeriodComponent comp, String clientId) throws ParseException {
comp.getChildren().clear();
DeliveryPeriod value = (DeliveryPeriod) comp.getAttributes().get("value");
List<PlanningPeriodSubset> pp = (List<PlanningPeriodSubset>) comp.getAttributes().get("periods");
HtmlPanelGrid pGrid = new HtmlPanelGrid();
pGrid.setColumns(4);
Calendar cBegin = new Calendar();
cBegin.setShowOn("button");
cBegin.setValue(sdf.parse(value.getStartDate()));
cBegin.setPattern("dd.MM.yyyy");
pGrid.getChildren().add(cBegin);
Calendar cEnd = new Calendar();
cEnd.setShowOn("button");
cEnd.setValue(sdf.parse(value.getEndDate()));
cEnd.setPattern("dd.MM.yyyy");
pGrid.getChildren().add(cEnd);
HtmlSelectOneMenu sPlanningPeriod = new HtmlSelectOneMenu();
Collection<UISelectItem> items = new ArrayList<UISelectItem>();
for (PlanningPeriodSubset op : pp) {
UISelectItem item = new UISelectItem();
item.setItemLabel(op.getName());
item.setItemValue(op.getId());
items.add(item);
}
sPlanningPeriod.getChildren().addAll(items);
sPlanningPeriod.setValue(value.getPlanningPeriodId());
pGrid.getChildren().add(sPlanningPeriod);
HtmlPanelGrid buttonPanel = new HtmlPanelGrid();
buttonPanel.setColumns(2);
Button bDelete = new Button();
bDelete.setValue(" - ");
buttonPanel.getChildren().add(bDelete);
Button bInfo = new Button();
bInfo.setValue(" i ");
buttonPanel.getChildren().add(bInfo);
pGrid.getChildren().add(buttonPanel);
comp.getChildren().add(pGrid);
}
#Override
public void decode(FacesContext context, UIComponent component) {
DeliveryPeriodComponent deliveryComponent = (DeliveryPeriodComponent) component;
DeliveryPeriod deliveryPeriod = new DeliveryPeriod();
deliveryPeriod.setStartDate(deliveryComponent.getBegin());
deliveryPeriod.setEndDate(deliveryComponent.getEnd());
deliveryPeriod.setPlanningPeriodId(deliveryComponent.getPlanningPeriod());
// Map<String, String> params = context.getExternalContext().getRequestParameterMap();
// String clientId = component.getClientId();
// String value = params.get(clientId);
// ((UIInput) component).setSubmittedValue(value);
}
#Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)
throws ConverterException {
Converter converter = getConverter(context, (DeliveryPeriodComponent) component);
if (converter != null) {
return converter.getAsObject(context, component, (String) submittedValue);
} else {
return submittedValue;
}
}
private Object getValue(FacesContext context, DeliveryPeriodComponent comp) {
Object submittedValue = comp.getSubmittedValue();
if (submittedValue != null) {
return submittedValue;
}
Object begin = comp.getBegin();
Object end = comp.getEnd();
Object planningPeriod = comp.getPlanningPeriod();
DeliveryPeriod period = new DeliveryPeriod();
period.setStartDate((String) begin);
period.setEndDate((String) end);
period.setPlanningPeriodId((Integer) planningPeriod);
Converter converter = this.getConverter(context, comp);
if (converter != null) {
return converter.getAsString(context, comp, period);
} else if (period != null) {
return period.toString();
} else {
return "";
}
}
private Converter getConverter(FacesContext context, DeliveryPeriodComponent comp) {
Converter conv = ((UIInput) comp).getConverter();
if (conv != null) {
ValueExpression exp = comp.getValueExpression("value");
if (exp == null) {
return null;
} else {
Class valueType = exp.getType(context.getELContext());
if (valueType == null) {
return null;
} else {
return context.getApplication().createConverter(valueType);
}
}
}
return null;
}}
Maybe this is a trivial problem, I am not a pro at JSF and still learning. And it is very hard to write your own components when you almost confused about the basics :-( Still learning new things every day. Thank you for your help!
(I found a lot on custom components with examples like a custum inputfields, these components works well and transfer their data. Unfortunatly I found nothing on custom components which contains more then one input field or did something different from the existing JSF elements)
In Xhtml Page Call your listner with
<h:commandButton id="add" value="Add More" styleClass="input_right_cor" type="button">
<f:ajax execute="#this" listener="#{templateSearchHandler.AddRow}" />
</h:commandButton>
Most of my action methods return PartialViews on success and RedirectToAction results on failure. For that, I would like to copy the model state errors into TempData so I could display them to the user. I've read several questions here on SO and some external links but none of them worked for me... I'm decorating the ActionMethod with ModelStateToTempData attribute from MvcContrib, then displaying it as follows in the view: (this is just a prototype)
#if (TempData.Count > 0)
{
foreach (var obj in TempData)
{
var errors = ((ModelStateDictionary)obj.Value).Values;
foreach (var error in errors)
{
<div style="position:absolute; background:Black; color:White; top:250px; left:550px;">
<span style="margin-bottom:5px; display:block; height:25px;">#error.Value</span>
</div>
}
}
}
Rather than displaying the error itself, I keep getting System.Web.Mvc.ValueProviderResult. I know this is all wrong, and eventually I would want to filter the model state errors into a dictionary inside the TempData but for now I just want to have the error string displayed in the view.
P.S: I've tried to do it manually without the MvcContrib attribute, and I got the same result. But I do prefer to use my own code so I could have more control over the whole issue.
Any suggestions?
Ok After trying a million things, I found the answer myself... :)
if (TempData["ModelErrors"] == null)
TempData.Add("ModelErrors", new List<string>());
foreach (var obj in ModelState.Values)
{
foreach (var error in obj.Errors)
{
if(!string.IsNullOrEmpty(error.ErrorMessage))
((List<string>)TempData["ModelErrors"]).Add(error.ErrorMessage);
}
}
return RedirectToAction("Index", "Home");
And in the view:
<div id="validationMessages">
#{
var errors = (List<string>)TempData["ModelErrors"];
}
#if (errors != null && errors.Count() > 0)
{
<div style="position:absolute; background:Black; color:White; top:250px; left:550px;">
#foreach (var error in errors)
{
<span style="margin-bottom:5px; display:block; height:25px;">#error</span>
}
</div>
}
</div>
UPDATE:
Here it is inside an ActionFilter:
public class CopyModelStateErrorsToTempData : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Only export when ModelState is not valid
if (!filterContext.Controller.ViewData.ModelState.IsValid)
{
//Export if we are redirecting
if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
{
if (filterContext.Controller.TempData["ModelErrors"] == null)
filterContext.Controller.TempData.Add("ModelErrors", new List<string>());
foreach (var obj in filterContext.Controller.ViewData.ModelState.Values)
{
foreach (var error in obj.Errors)
{
if (!string.IsNullOrEmpty(error.ErrorMessage))
((List<string>)filterContext.Controller.TempData["ModelErrors"]).Add(error.ErrorMessage);
}
}
}
}
base.OnActionExecuted(filterContext);
}
}
I started going down this road, and then read your answer. I combined them into the following files:
TempDataDictionaryExtensions.cs
I created extension methods to do the dirty work on the TempData, because I felt it didn't belong in the Action Filter itself.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace Project.Web.UI.Domain
{
public static class TempDataDictionaryExtensions
{
private const string _ModelStateErrorsKey = "ModelStateErrors";
public static IEnumerable<string> GetModelErrors(this TempDataDictionary instance)
{
return TempDataDictionaryExtensions.GetErrorsFromTempData(instance);
}
public static void AddModelError(this TempDataDictionary instance, string error)
{
TempDataDictionaryExtensions.AddModelErrors(instance, new List<string>() { error });
}
public static void AddModelErrors(this TempDataDictionary instance, IEnumerable<string> errors)
{
TempDataDictionaryExtensions.AddErrorsToTempData(instance, errors);
}
private static List<string> GetErrorsFromTempData(TempDataDictionary instance)
{
object tempObject = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey);
if (tempObject == null)
{
return new List<String>();
}
List<string> tempErrors = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey).Value as List<string>;
if (tempErrors == null)
{
return new List<String>();
}
return tempErrors;
}
private static void AddErrorsToTempData(TempDataDictionary instance, IEnumerable<string> errors)
{
List<string> tempErrors;
object tempObject = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey);
if (tempObject == null)
{
tempErrors = new List<String>();
}
else
{
tempErrors = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey).Value as List<string>;
if (tempErrors == null)
{
tempErrors = new List<String>();
}
}
tempErrors.AddRange(errors);
instance[TempDataDictionaryExtensions._ModelStateErrorsKey] = tempErrors;
}
}
}
TempDataModelStateAttribute.cs
My original, copied the errors out of TempData back into ModelState prior to the ActionResult executing via OnResultExecuting. This is a combination of copying them into TempData and back out.
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace Project.Web.UI.Domain
{
public class TempDataModelStateAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
IEnumerable<string> modelErrors = ((Controller)filterContext.Controller).TempData.GetModelErrors();
if (modelErrors != null
&& modelErrors.Count() > 0)
{
modelErrors.ToList()
.ForEach(x => ((Controller)filterContext.Controller).ModelState.AddModelError("GenericError", x));
}
base.OnResultExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!filterContext.Controller.ViewData.ModelState.IsValid)
{
if (filterContext.Result is RedirectResult
|| filterContext.Result is RedirectToRouteResult)
{
List<string> errors = new List<string>();
foreach (var obj in filterContext.Controller.ViewData.ModelState.Values)
{
foreach (var error in obj.Errors)
{
errors.Add(error.ErrorMessage);
}
}
((Controller)filterContext.Controller).TempData.AddModelErrors(errors);
}
}
base.OnActionExecuted(filterContext);
}
}
}
You should seriously consider this concept:
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg