I'm trying to set up Log4Net in a MVC.net project with Ninject.
I've reached the point where I'm able to see the Ninject debug output but I am still not seeing any logging output.
This is all running on my localhost so there shouldn't be any issues with security.
The setup I have is as follows:
In the AssemblyInfo.cs I have added this to point log4net to Web.config for configuration
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Web.config", Watch = true)]
In the Web.config I've added the following sections
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
</configSections>
...
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
...
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="log-file.txt" />
<appendToFile value="true" />
<encoding value="utf-8" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
<root>
<level value="ALL" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
In my base controller, from which all the other controllers in the system inherit I have the following:
[Inject]
public ILogger logger { get; set; }
I then put an explicit throw new Exception("testException") on the login page to force an error which I would have expected to see showing up in the log file.
From the debug trace I see the following on startup.
log4net: log4net assembly [log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821]. Loaded from [C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\portal\63a118bb\781d84b7\assembly\dl3\40d137a6\9fb44da2_c951cc01\log4net.dll]. (.NET Runtime [4.0.30319.235] on Microsoft Windows NT 6.1.7601 Service Pack 1)
log4net: DefaultRepositorySelector: defaultRepositoryType [log4net.Repository.Hierarchy.Hierarchy]
log4net: DefaultRepositorySelector: Creating repository for assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7]
log4net: DefaultRepositorySelector: Assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7] Loaded From [C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\portal\63a118bb\781d84b7\assembly\dl3\edd36351\91af46a2_c951cc01\Ninject.Extensions.Logging.Log4Net.dll]
log4net: DefaultRepositorySelector: Assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7] does not have a RepositoryAttribute specified.
log4net: DefaultRepositorySelector: Assembly [Ninject.Extensions.Logging.Log4Net, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7] using repository [log4net-default-repository] and repository type [log4net.Repository.Hierarchy.Hierarchy]
log4net: DefaultRepositorySelector: Creating repository [log4net-default-repository] using type [log4net.Repository.Hierarchy.Hierarchy]
I have the feeling that this is probably a configuration issue but any help would be appreciated.
Injecting an ILog or ILogger instance does not implicitly register any kind of exception handler. It just gives you access to a logger. You still have to tell it what to log and when to log it.
If you want to test your configuration, make a logging call from one of your controller's actions, or use one of the OnAction event invokers. This should probably do what you want:
public class MyController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext context)
{
if (Logger != null)
{
string message = string.Format("Action executed: {0}.{1}",
context.ActionDescriptor.ControllerDescriptor.ControllerName,
context.ActionDescriptor.ActionName);
Logger.Log(typeof(MyController), log4Net.Core.Level.Trace,
message, context.Exception);
}
base.OnActionExecuted(context);
}
[Inject]
public ILogger Logger { get; set; }
}
That will log every controller action - including exceptions. If you only care about exceptions then either check the context.Exception and context.ExceptionHandled properties before logging, or better yet write a custom ActionFilter to do this.
Related
I have created a project call Repository.EF to handle Data access in a n-tire solution. I have added EF to the Repository.EF project where I have all my POCO's . Then I created a DbContext class in that project like this.
namespace LearningSpike.Repositories.EF
{
class GlassContractDbContext:DbContext
{
public GlassContractDbContext() : base("GlassContractContext")
{
}
public DbSet<MetalStock> MetalStock { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new MetalStockConfiguration());
}
}
Then went to package manager console and did
Enable-Migrations
Then set the
AutomaticMigrationsEnabled = true;
Then
Update-Database
Everything is working fine. But the problem is, I don't know where the connection string is. It seems like there is no connectionString in that particular project. I know if I had an MVC4/5 template, there will be a connectionString in the web.config. How can I find the connection string?
How do I configure things now? For example I remember doing this with the connectionString in a MVC5 app
MultipleActiveResultSets=true
How do I do it now?
Thanks!
Cheers!
PS
Also I have the following code in my App.config in the Repository.EF Project
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,
EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit
http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory,
EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient"
type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
When running the package manager console it will default to use the start-up project (unless you specify the -project param in the command or use the drop down in console manager). From there it will look for your connection strings in the config files of that project. If that is a web project this will be in the web.config.
If you have not added your own connection string to that project, EF will be able to use its own one derived from the project name and create an mdf file which it attaches on the fly during runtime.
If you want to add a connection string (you can do this is any of your config files - however by the sounds of it you want to add this to the app.config in your datalayer project) you can add this below your config sections:
<connectionStrings>
<add name="MyDatabase"
providerName="System.Data.SqlClient"
connectionString="Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;"/>
</connectionStrings>
I am having the architecture like this :
Where MVC layer is the presentation layer. EF is class library and Repository is another class library.I am trying to insert data to database from repository by creating the EF context object. Added EF reference into Repository class library. EF having the edmx file. its app.config having the connection string generated by EF.
code is :
public bool CreateUser(User _user)
{
context.Users.Add(_user);
context.SaveChanges();
return true;
}
but while executing this I am getting following exception :
No connection string named 'MyEntitiesConnection' could be found in the application config file.
I tried to add same connection string with same name in repository app.config. but not working. anyone have solution ?
Edited:
connection string is :
<add name="MyEntitiesConnection" connectionString="metadata=res://*/EF.Entities.csdl|res://*/EF.Entities.ssdl|res://*/EF.Entities.msl;provider=System.Data.SqlClient;provider connection string="data source=Servername\MSSQL2008R2;initial catalog=MyDBName;persist security info=True;user id=sa;MultipleActiveResultSets=True;App=EntityFramework;" providerName="System.Data.EntityClient" />
app.config:
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="MyEntitiesConnection" connectionString="metadata=res://*/EF.Entities.csdl|res://*/EF.Entities.ssdl|res://*/EF.Entities.msl;provider=System.Data.SqlClient;provider connection string="data source=Servername\MSSQL2008R2;initial catalog=MyDBName;persist security info=True;user id=sa;MultipleActiveResultSets=True;App=EntityFramework;" providerName="System.Data.EntityClient" />
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
</configuration>
In any .NET application, only one config file is the natural starting point for looking for configuration information. For web applications, that's the web.config file at the root of the application1.
Whilst you may have a file called app.config in your repository project (and, indeed, some VS tooling may have added one) or your EF project, it's not used when you try to read configuration information.
The connection string section needs to exist in the web.config of your MVC app.
1For non-web applications, it's the app.config for the project that produces the .exe file and that gets automatically copied as XXX.exe.config during the build.
To the mods: not a duplicate, other answers (mainly "check the web.config in the Views folder") didn't work.
I have a main asp.net MVC application which discovers controllers and views in a separate assembly: controllers with MEF and a custom ControllerFactory, views with a custom VirtualPathProvider which discovers the views embedded in the same (of the controller) assembly.
The discovery of both parts works good: the controller factory finds the controller and initializes it using Ninject, the VirtualPathProvider recognizes that it has to look for the view in the assembly and loads it.
The web.config in the Views folder of the separate assembly is the one VS2013 generated when creating a new asp.net mvc project:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="Fos.WS.DemoCustomization" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
</configuration>
Controller:
public ActionResult Index()
{
var dll = "~/Plugins/" + this.AssemblyName; //"~/Plugins/" is to instruct the custom viewpathprovider to discover the view from the assembly
return View(dll + "/Fos.WS.DemoCustomization.Views.Demo.Index.cshtml");
}
View:
#{
ViewBag.Title = "Demo";
}
<h2>Hi, Demo</h2>
The error i keep getting is
Compiler Error Message: CS0103: The name 'ViewBag' does not exist in the current context
Source Error:
Line 38: public override void Execute() {
Line 39:
Line 40: ViewBag.Title = "Demo";
Line 41:
Line 42: BeginContext("~/Plugins/Fos.Ws.DemoCustomization.dll/Fos.WS.DemoCustomization.Views.Demo.Index." +
Source File: c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\482869c5\3f1d4579\App_Web_fos.ws.democustomization.views.demo.index.cshtml.41ce77d0.bg7gacz2.0.cs Line: 40
Thanks
Edit: tried to remove the viewbag part and keep just the , now the error is different:
The view at '~/Plugins/Fos.Ws.DemoCustomization.dll/Fos.WS.DemoCustomization.Views.Demo.Index.cshtml' must derive from WebViewPage, or WebViewPage.
It looks like the web.config isn't there.
Answers a little late, but I think I may be able to add a bit of insight. So i haven't worked in this exact scenario, but i have worked with MEF, and dynamic View rendering separately.
Obvious
So an obvious issue if i'm reading your setup correctly is you're relying on that web.config defined above but it is in the remote assembly. I don't believe your IOC containers are able to access the assembly definitions from the remote config.
You even recognize that it is not picking up the web config values in your question. I know you said you had tried other web config resolutions without success, but I think that will be your best bet because it needs to pull in those definitions somehow, and trying to define those manually is going to be a hack.
Less Obvious
The reason you're seeing those exceptions is the ViewData object is defined by the WebViewPage base class, your config as you can see defines that as your base class for all views. without that base class, it's not a view and not parsable.
The reason I think it was hiding this error when accessing the ViewBag explicitly is the ViewBag is a wrapper in the ViewContext, passing ViewData to a DynamicViewDataDictionary. The ViewContext won't build if you pass in null ViewData, I think by calling the ViewBag explicitly on a null ViewContext it was just blowing up earlier in the chain, or rather the compiler recognized the issue this way.
Utility Helper
Since I am sorry I can't offer a silver bullet for this issue. Just more info pointing to the web.config not pulling in. I'm going to add a fun helper, that is also a hack for getting remote/dynamic Razor Views to parse manually from a file. You can also use this to make sure remote Views are well formatted and see how they will output. If you plugged this in I bet since it's defining a RazorView which manages the WebPageView initialization in this case. It'll work for your remote Views. worth noting this is a hack and not ideal for handling all of your View Rendering but is certainly handy in some sticky corner cases.
using System.IO;
using System.Web.Mvc;
namespace Utils
{
public class RazorUtility
{
/// <summary>
/// Takes a file path to a Razor View and a controller context and renders the the View to a string
/// </summary>
/// <param name="path">file path to view</param>
/// <param name="context">controller context to render against</param>
/// <returns></returns>
public static string RenderView(string path, ControllerContext context)
{
//You need to include the Model in the ViewDate of the context passed in.
var st = new StringWriter();
var razor = new RazorView(context, path, null, false, null);
razor.Render(new ViewContext(context, razor, context.Controller.ViewData, context.Controller.TempData, st), st);
return st.ToString();
}
}
}
My log4net configuration is this,
<?xml version="1.0" encoding="utf-8" ?>
<log4netConfiguration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<appSettings>
<add key="log4net.Config" value="log4net.config" />
</appSettings>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\my_logs/my_web_logs/my_log_%date{ddMMyyyy}.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="30MB" />
<datePattern value="yyyyMMdd" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n"/>
<!--<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] – %message%newline" />-->
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
</log4netConfiguration>
I have a Logger helper class as,
public static class Logger
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static log4net.ILog Log
{
get { return log; }
}
}
In my assembly info, I have this entry,
// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
log4net.config is the config file added to the web project.
In the code I log using the logger class,
Logger.Log.Info("User visits Sign In Page.");
Logging has been working when I set up the above setting. But suddenly logging has stopped working.
But when I created a new asp.net mvc website with above settings, logging works for that.
I tried with IIS Express and Local IIS. In both cases logging works for the test application I have created.
I cannot figure out why it's not logging? How can I diagnose this? What are the possible issues?
Solved by myself, reason was "for some reason" log4net configuration was not loaded from assembly info. Still I do not know why that happens.
I tried so many fixes proposed by different posts. Finally fixed the issue.
Solution mentioned in this post helped me to solve the issue.
I have added following configuration,
<!--These settings load the log4net configuration.-->
<add key="log4net.Config" value="log4net.config"/>
<add key="log4net.Config.Watch" value="True"/>
It starts logging!
Then I removed following line from assembly info,
// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
The reason is that Log4Net tries to load the config from the assembly that first uses LogManager.GetLogger(). If it's one of your class libraries it will simply ignore the attribute in all other assemblies.
The easiest way to fix it is to invoke LogManager in your start file (like Program.cs or Global.asax):
var logger = LogManager.GetLogger(typeof(Program));
logger.Info("Application started.");
//rest of app init.
Doing that will get you the expected behavior with the assembly attribute.
I found log4net won't load the webconfig configuration unless you call log4net.Config.XmlConfigurator.Configure(); on start up.
protected void Application_Start()
{
// your other codes
log4net.Config.XmlConfigurator.Configure(); // must have this line
Logger = log4net.LogManager.GetLogger(typeof(MvcApplication));
}
I had a similar issue: I would not get any logging output, when I ran my assembly from IIS or IISExpress.
However, none of the answers above worked for me.
In my case the solution was to specify the path to the config file as an absolute path. It turned out that IISExpress does not set the current directory to the bin folder and log4net would not find the config file, so I had to use this workaround:
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
// remove file:// part from uri
UriBuilder ub = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(ub.Path);
var fi = new FileInfo(Path.Combine(Path.GetDirectoryName(path), "Logging.config.xml"));
if (fi.Exists)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(fi);
var logger = log4net.LogManager.GetLogger(typeof(WebApiApplication));
logger.Info("Application started.");
}
else
{
throw new FileNotFoundException("log4net config file not found", fi.FullName);
}
I'm starting a new project for work, and I decided I want to give MVC a shot. It's a small internal site for a commute challenge.
I want to use Spring.NET for Validation. I have used Spring.NET before in Web Forms, but with no code behind as in traditional ASP.NET, how do I use the Page Validation framework Spring.NET provides?
Edit 1:
In an attempt to try this myself, here is what I have:
Web.Config
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<appSettings>
<add key="RouteValidator" value="RouteValidator"/>
<add key="UserValidator" value="UserValidator"/>
</appSettings>
<spring>
<context>
<resource uri="config://spring/objects"/>
<resource uri="~/Config/Spring.Web.cfg.xml" />
<resource uri="~/Config/Spring.Validation.cfg.xml" />
</context>
<parsers>
<parser type="Spring.Validation.Config.ValidationNamespaceParser, Spring.Core" />
</parsers>
</spring>
<system.web>
<httpModules>
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web" />
</httpModules>
</system.web>
</configuration>
Spring.Web.Cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<description>
Foo MVC Controller declarations.
</description>
<object id="HomeController" type="Foo.MVC.Web.Controllers.HomeController, Foo.MVC.Web"></object>
<object id="AccountController" type="Foo.MVC.Web.Controllers.RouteController, Foo.MVC.Web"></object>
<object id="RouteController" type="Foo.MVC.Web.Controllers.RouteController, Foo.MVC.Web"></object>
<object id="Spring.Web.UI.Controls.ValidationError" abstract="true">
<property name="Renderer">
<object type="Spring.Web.UI.Validation.IconValidationErrorsRenderer, Spring.Web">
<property name="IconSrc" value="validation-error.gif"/>
</object>
</property>
</object>
<object id="Spring.Web.UI.Controls.ValidationSummary" abstract="true">
<property name="Renderer">
<object type="Spring.Web.UI.Validation.DivValidationErrorsRenderer, Spring.Web">
<property name="CssClass" value="validationError"/>
</object>
</property>
</object>
<object id="standardPage" abstract="true">
<property name="MasterPageFile" value="~/Views/Shared/Site.master"/>
<property name="CssRoot" value="~/Content/"/>
<property name="ImagesRoot" value="~/Content"/>
</object>
</objects>
My validation file is very standard and basically a copy and paste from another project, therefore I didn't include it.
Now the problem I have is how do I use it? How do I get application context? My web forms project users Spring.Web.UI.Page, but I'm worried because the default pages in MVC derive from System.Web.Mvc.ViewPage, so that isn't going to work.
Or am I just not able to use Spring.NET's framework for MVC quite yet?
Thanks!
Thanks for any assistance.
You definitely can use Spring with ASP.Net MVC. You need to register that you are using it in the Global.ascx class then the framework will create Controllers based on what you have defined in your config file.
public class MvcApplication : System.Web.HttpApplication
{
...Routes stuff...
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
RegisterRoutes(RouteTable.Routes);
}
}
public class ControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext requestContext, string controllerName)
{
return IoC.Resolve<IController>(controllerName);
}
public void ReleaseController(IController controller)
{
//This is a sample implementation
//If pooling is used write code to return the object to pool
if (controller is IDisposable)
{
(controller as IDisposable).Dispose();
}
controller = null;
}
}
public static class IoC
{
static readonly IObjectFactory Factory
= new XmlObjectFactory(new FileSystemResource
(HttpContext.Current.Server.MapPath("~/Config/Spring.config")));
public static T Resolve<T>(string name)
{
return (T)Factory.GetObject(name);
}
}
Just make sure that the path to your spring config file is correct! This was adapted from this link.
On a wider note, this approach does not allow you to Spring the page classes, and being an MVC architecture, where views are pretty dumb classes, does not really support rich validation in the view itself in the manner you suggest. Look at either including the validation in the Model (post-back) in JQuery.
In best of my knowledge, the Spring Validation is not supported up to the latest release of ASP.NET MVC (1.0) and Spring.NET framework (1.3).
As far as incorporating Spring.NET with MVC, you can use MvcContrib project and come to the same code-base as posted by Colin Desmond (but you don't have to do the dirty work yourself).