The team and I are looking at Orchard to use as our CMS. We have a fairly large ASP.NET 4.0 WebForms app and we can only migrate the bits one piece at a time. With that in mind, we will still serve our aspx pages and slowly convert them into the CMS as time allows.
I've noticed on the docs # orchardproject.net/docs, link here, about how one does authorization inside a module. So all that has to be done is include IOrchardService in the constructor of a controller and it'll get injected at runtime.
public AdminController(IMyService myService, IOrchardServices orchardServices) {
_myService = myService;
Services = orchardServices;
}
Because the conversion process will be slow, ¿can I provide a constructor with the IOrchardServices on a System.Web.UI.Page subclass?
// something like this
public partial class Test : System.Web.UI.Page
{
IOrchardServices _service;
public Test(IOrchardServices orchardServices)
{
_service = orchardServices;
}
}
This does not work, throws an exception expecting a default constructor. If I place the default constructor, the page loads correctly but IOrchardServices is not injected. ¿Is this even possible?
Related
I'm adding a custom control dynamically in master page code-behind:
try
{
// Add custom sidenav menu control dynamically
SideNavMenu sidenav = new SideNavMenu();
tempPath = Request.RawUrl.ToLower();
path = tempPath.Contains(#"/sitename") ? tempPath.Substring(7) : tempPath;
sidenav.MenuPath = path;
menuPlaceHolder.Controls.Add(sidenav);
}
catch
{
// Handle this - custom error form and email
// Master page needs access to base page LogError method
}
This is the base page with ErrorLog() method, which actually generates an email:
public partial class BasePage : System.Web.UI.Page
{
public void LogError(Exception error)
{
...
smtpClient.Send(message);
}
}
How can I call ErrorLog from master page code-behind? Or is there a better place to put the "common" ErrorLog method? Can someone suggest correct syntax or a better approach?
Place the LogError function in a class in your App_Code folder and probably make it a static function. If it's reusable through multiple projects, then place it in a seperate class library. Your BasePage class has no business implementing the details of logging errors and sending emails. Remember Separation of Concerns (SoC).
In fact, a library already exists to do this for you, called Elmah.
I highly recommend NLog for your purposes. It allows for configurable logging, including via SMTP. Your code would look like:
using Nlog;
public partial class BasePage : System.Web.UI.Page
{
private static Logger bpLogger = LogManager.GetCurrentClassLogger();
public void LogError(Exception error)
{
bpLogger.LogException(
LogLevel.Error,
"ruh roh",
error
);
...
//smtpClient.Send(message);
}
}
Because NLog is pretty efficient, you can instantiate a private static Logger object per page you are logging on. Additionally, the logger will automatically record which class it came from, and various configuration options allow you to change where it logs, how it logs and when it logs without changing any other code.
Here is a link to the configuration for sending errors via SMTP, for example:
https://github.com/nlog/NLog/wiki/Mail-target
It's a mature project that has been around for a very long time and can grow with your project as your logging needs grow.
I am using VS2008 and have a solution file which contains 1 Website and 1 Class Library Project.
The Class Library is a Custom Control which derives from Label. The Website contains a reference to the control - it builds successfully and the compiled .dll gets added to the Website's /bin folder. I can then use it in any of the website's .aspx pages without error.
What I cannot do, however, is reference any of the Website's data access methods that are in static classes in /App_Code from within the custom control.
I don't want to repeat the website data access logic all over again within the custom control when I know it will already exist in the website - I just want to be able to call a method from a class in /App_Code . If I try and reference anything in /App_Code from within the Class Library, it fails to build and says it can't find the Namespace or that it doesn't exist in the current context.
How can I achieve this so that the Custom Control builds as a standalone control, but can make use of classes in the website it gets used in? Delegates, possibly? Was hoping it might be more straightforward than that.
EDIT: I should add that the reason the control is in a separate Class Library is so that I can include JavaScript as an embedded resource within the Control. So when it's used in a .aspx page, it adds a WebResource.axd? style link to the page instead of a load of plaintext JavaScript in the <head> section.
EDIT 2:
In the website App_Code folder, I have a static class that handles data access (snippet):
[DataObject]
public static class DBAccess
{
[DataObjectMethod(DataObjectMethodType.Select)]
public static DataTable GetSomeData(Int32 SomeParam, DateTime OtherParam)
{
SqlConnection cn = SqlLibrary.GetConnection(DBConnectionString);
DataTable _dt;
SqlLibrary.SProcFill(out _dt, cn, "usp_SomeData_Select", SomeParam, OtherParam);
return _dt;
}
}
In the Class Library's custom control (which I want to build independently of the website's existence, yet be capable of calling its methods when used as a control in an .aspx page):
namespace MyCustomControls
{
public class StatusControl : Label
{
private Int32 _someProperty = -1;
private DateTime _otherProperty = DateTime.Now;
public StatusControl()
{
//some constructor logic
}
public void FetchData()
{
//what I'd **like** to do here is:
DBAccess.GetSomeData(_someProperty, _otherProperty);
//...but DBAccess isn't "visible" to this control at build time
}
}
}
I'm a first-time user of the AOP features of Unity 2.0 and would like some advice. My goal is to be able to log method calls in an ASPX page, like so:
public partial class Page2 : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
[Log]
private void Testing()
{
}
}
Here is the code for the LogAttribute:
public class LogAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogHandler(Order);
}
}
Now the LogHandler:
public class LogHandler : ICallHandler
{
public LogHandler(int order)
{
Order = order;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
string className = input.MethodBase.DeclaringType.Name;
string methodName = input.MethodBase.Name;
string preMethodMessage = string.Format("{0}.{1}", className, methodName);
System.Diagnostics.Debug.WriteLine(preMethodMessage);
return getNext()(input, getNext);
}
public int Order { get; set; }
}
The problem I have is how to use the [Log] attribute. I've seen plenty of example of how to configure the interception settings, for example:
container.AddNewExtension<Interception>();
container.Configure<Interception>().SetDefaultInterceptorFor<ILogger>(new InterfaceInterceptor());
But this implies that I have an interface to intercept, which I don't. I have the ASPX page which uses the [Log] attribute.
so how can I configure Unity to make use of the [Log] attribute? I've done this before using PostSharp and would like to be able to use Unity to do the same.
Cheers.
Jas.
You're unfortunately not going to get this to work in an ASP.NET page with Unity interception.
Unity interception uses a runtime interception model. Depending on the interceptor you choose, you'll either get a subclass with virtual method overrides to call the call handlers (VirtualMethodInterceptor) or a separate proxy object (Interface or TransparentProxyInterceptor) which execute the call handlers and then forward to the real object.
Here's the issue - ASP.NET controls creation and calls to your page, and there's no easy way to hook into them. Without controlling the creation of the page object, you can't use the VirtualMethodInterceptor, because that requires that you instantiate a subclass. And you can't use the proxy version either, because you need ASP.NET to make calls through the proxy.
PostSharp gets around this because it's actually rewriting your IL at compile time.
Assuming you could hook into the creation of the page object, you'd have to use the VirtualMethodInterceptor here. It's a private method, so you want logging on "self" calls (calls from one method of the object into another method on the same object). The proxy-based interceptors can't see those, since the proxy is a separate instance.
I expect there is a hook somewhere to customize how ASP.NET creates object - BuildManager maybe? But I don't know enough about the details, and I expect it'll require some pretty serious hacking to get work.
So, how do you get around this? My recommendation (actually, I'd recommend this anyway) is to use the Model-View-Presenter pattern for your ASP.NET pages. Make the page object itself dumb. All it does is forward calls to a separate object, the Presenter. The Presenter is where your real logic is, and is independent of the details of ASP.NET. You get a huge gain in testability, and you can intercept calls on the presenter without all the difficulty that ASP.NET gives you.
How to declare a global variable or a public sub in a web application that all aspx pages can have access to?
use a static variable in one of your code files.
"Global" variables can be kept in Cache using Cache.Add, or Application state using Application.Add.
"Globally-available" methods are generally an antipattern and should be avoided. If you need a utility function you can add a static method to a class, but beware the Ball of Mud antipattern.
1.You can use session variables which will be available to all pages in the scope of current session.
C#
Session("name")=value;
2.You can use application variables which will be available to entire application code untill application ends.
Application("name") = value;
Global.asax inherits from YourWebSiteApplicationClass...
public class YourWebSiteApplicationClass : HttpApplication
{
public string GlobalVariable;
public YourWebSiteApplicationClass()
{
GlobalVariable = "GLOBAL_VARIABLE";
}
}
...and in any .aspx or .cs(.vb) file...
<% = ((YourWebSiteApplicationClass)this.ApplicationInstance).GlobalVariable %>
Return "GLOBAL_VARIABLE".
Create a PageBase class and have your pages inherit from it.
i'm trying to work out the best method to perform logging in the application i'm currently developing.
right now, i have a Log table that stores the username, timestamp, action, controller, and a message. when a controller is instantiated, it gets the IoC info through Castle Windsor.
for example, my "Sites" controller is created as follows:
private ISitesRepository siteRepository;
private ILogWriter logWriter;
public SiteController(ISitesRepository siteRepository, ILogWriter logWriter)
{
this.siteRepository = siteRepository;
this.logWriter = logWriter;
}
and the log writer has a function that creates and inserts a log entry (WriteToLog). within the Sites controller's Edit and Create actions, it calls the WriteToLog function.
this is working and doing its job, but my question is- do i really need to set up each controller this way, passing through the ILogWriter interface/repository? it struck me that i could possibly set up a LogController, and just have that do the "heavy lifting" of writing to my logs.
that way, i wouldn't have to mess with the IoC stuff in every other controller. is it possible to execute an action on another controller (for example, a LogController-> WriteLog)? i'm not sure how would that be done without doing a redirect...
Could you pass by an abstract class? This abstract class having a static property referencing you log writer?
something like this
public abstract class BaseController
{
public static ILogWriter Logwriter{get;set;}
public static BaseController
{
Logwriter = YourFactory.GetLogwriter();
}
}
public class YourController:BaseController
{
public YourController(ISitesRepository siteRepository)
{
}
}
Ok, after much head scratching, i think i found an acceptable solution.
I implemented my logging action as a custom action filter as so:
public class LogAction : ActionFilterAttribute, IActionFilter
{
public LogLevel loglevel;
public string message;
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ILogWriter logWriter = AppServiceFactory.Instance.Create<ILogWriter>();
logWriter.WriteToLog(
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
filterContext.ActionDescriptor.ActionName,
loglevel,
filterContext.HttpContext.Timestamp,
filterContext.HttpContext.User.Identity.Name.ToString(),
message + "(id=" + filterContext.RouteData.Values["id"] + ")");
}
}
but i ran into a wall trying to get the IoC to work in a custom attribute filter. scouring stackoverflow and google searches, i found that it's sort of difficult to do, with talk about using different wrappers, action invokers, etc, which all seemed more complicated than i was really willing to deal with.
trying to learn more about IoC (i'm still very new at this), i found this article,
which really helped point me in the right direction. i added his sealed AppServiceFactory class with my WindsorControllerFactory, and it worked like a charm.
As i said, i'm very new with to MVC and this IoC stuff, so i'm not sure this is an ideal way of handling things- but it seems simple and it works so far. I'd welcome any comments or criticisms on handling it through this method.
UPDATE
Figured out a different way of doing this- created a function in my WebUI project as such:
public static class Loggers
{
public static void WriteLog(ControllerContext controllerContext, LogLevel logLevel, string message)
{
ILogWriter logWriter = AppServiceFactory.Instance.Create<ILogWriter>();
logWriter.WriteToLog(
controllerContext.RouteData.Values["controller"].ToString(),
controllerContext.RouteData.Values["action"].ToString(),
logLevel,
controllerContext.HttpContext.Timestamp,
controllerContext.HttpContext.User.Identity.Name.ToString(),
message);
}
}
now, wherever i want to log something, i can call
Loggers.WriteLog(
this.ControllerContext,
LogLevel.Membership,
"Removed role '" + role + "'" + " from user " + _userService.Get(id).UserName );
to write a record to the log. this gives me a lot more flexibility on my "message" content, and solves the problem of including logging in the global.asax file, which would've been difficult if not impossible using the attribute filters. i'll leave the rest, as it may be of use to someone else, but i think this is the way i'll go on this.
as usual, things are usually simpler in MVC than i original think they will be :)