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.
Related
In UWP, I would programmatically initialize display variables at startup so that the initial display was what I wanted, for example, values based on the day of the year value. I can't crack where to replicate this in my otherwise working Blazor webassembly app. I currently put up default values and ask the user to click a button, that calculates and updates to the values I want to display and on we go.
I would have thought there's a standard, easy way to do this, but given how Blazor pieces are constructed on the fly in order to render as web pages, maybe not? Thanks!
This isn't specific to Blazor, but in any class you can create code in the constructor of your component/
Here I have a Chat component, that I break up into
Chat.razor
Chat.razor.cs
The chat.razor.cs you create by adding a class with the same name as your component, then add the word partial to make it a partial class:
partial class Chat : IBlazorComponent, IBlazorComponentParent, IDisposable
Note: The interfaces are just for my app, I am just showing the line with partial.
really are you need it:
partial class Chat
Then in my constructor I created:
public Chat()
{
// Perform initializations for this object
Init();
}
I just call a method, you don't have to:
My Init method:
public void Init()
{
// do your initializations
}
Or you can use OnInitializedAsync
protected override async Task OnInitializedAsync()
{
// load the Categories (example)
this.Categories = await HelpCategoryService.GetHelpCategoryList()
}
Or here is another I use sometimes: OnAfterRender
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
...
}
}
Documentation for that is here:
https://learn.microsoft.com/en-us/aspnet/core/blazor/lifecycle?view=aspnetcore-3.1
Maybe that gives you some ideas. Not sure about Standard, every use case is different.
Note: I have never used Blazor Web Assembly, only server side Blazor, so not sure what is specific to WASM, but I know constructors work in any C# class.
I assume you want to have global Variables for your app:
Create a Initialize service that initializes your static data.
public class InitializeService
{
public InitializeService()
{
MyStaticDataClass.Variable1 == "Pling";
MyStaticDataClass.Variable1 == "GlobalPlong";
}
}
In startup services.AddSingleton().
Your MyStaticDataClass vars will now be available from your app and pages
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?
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.
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 :)