How to implement Serilog Custom Sink outputTemplate parameter - serilog

How do I implement outputTemplate that is common to most sinks?
For example this works:
Logger = new LoggerConfigruration()
.WriteTo.Console(outputTemplate: "{Timestamp}{message}")
.CreateLogger();
I want to
Logger = new LoggerConfigruration()
.WriteTo.MySink(outputTemplate: "{Timestamp}{message}")
.CreateLogger();
Where
public class MySink : ILogeventSink
{
private readonly IFormatProvider formatProvider;
private string outputTemplate;
public void Emit(LogEvent logevent)
{
//something here to do with outputTemplate???
}
public MySink(IFormatProvider format, string template)
{
formatProvider = format;
outputTemplate = template;
}
}

You can use MessageTemplateTextFormatter from Serilog.Formatting.Display.
public class MySink : ILogEventSink
{
private readonly MessageTemplateTextFormatter _formatter;
public MySink(IFormatProvider formatProvider, string outputTemplate)
{
_formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
}
public void Emit(LogEvent logEvent)
{
using var writer = new StringWriter();
_formatter.Format(logEvent, writer);
var message = writer.ToString()
Console.WriteLine(message);
}
}

Related

Inject into the Startup class

Is it possible to use an IOC framework like Castle Windsor to inject into the Startup method. I mean something like this:
public class Startup()
{
IMyObject MyObject = new MyObject();
public Startup(MyObject myObject)
{
MyObject = myObject();
}
}
I am trying to drop and create a database on startup using NHibernate. Alternatively is there a "better" place to drop and create the database using NHibernate?
I do something like this for integration tests using specflow.
I have a NHibernateInitializer class that I inherit in all my projects that looks like this
public abstract class NHibernateInitializer : IDomainMapper
{
protected Configuration Configure;
private ISessionFactory _sessionFactory;
private readonly ModelMapper _mapper = new ModelMapper();
private Assembly _mappingAssembly;
private readonly String _mappingAssemblyName;
private readonly String _connectionString;
protected NHibernateInitializer(String connectionString, String mappingAssemblyName)
{
if (String.IsNullOrWhiteSpace(connectionString))
throw new ArgumentNullException("connectionString", "connectionString is empty.");
if (String.IsNullOrWhiteSpace(mappingAssemblyName))
throw new ArgumentNullException("mappingAssemblyName", "mappingAssemblyName is empty.");
_mappingAssemblyName = mappingAssemblyName;
_connectionString = connectionString;
}
public ISessionFactory SessionFactory
{
get
{
return _sessionFactory ?? (_sessionFactory = Configure.BuildSessionFactory());
}
}
private Assembly MappingAssembly
{
get
{
return _mappingAssembly ?? (_mappingAssembly = Assembly.Load(_mappingAssemblyName));
}
}
public void Initialize()
{
Configure = new Configuration();
Configure.EventListeners.PreInsertEventListeners = new IPreInsertEventListener[] { new EventListener() };
Configure.EventListeners.PreUpdateEventListeners = new IPreUpdateEventListener[] { new EventListener() };
Configure.SessionFactoryName(System.Configuration.ConfigurationManager.AppSettings["SessionFactoryName"]);
Configure.DataBaseIntegration(db =>
{
db.Dialect<MsSql2008Dialect>();
db.Driver<SqlClientDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionString = _connectionString;
db.BatchSize = 20;
db.Timeout = 10;
db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
});
Configure.SessionFactory().GenerateStatistics();
Map();
}
public virtual void InitializeAudit()
{
var enversConf = new Envers.Configuration.Fluent.FluentConfiguration();
enversConf.Audit(GetDomainEntities());
Configure.IntegrateWithEnvers(enversConf);
}
public void CreateSchema()
{
new SchemaExport(Configure).Create(false, true);
}
public void DropSchema()
{
new SchemaExport(Configure).Drop(false, true);
}
private void Map()
{
_mapper.AddMappings(MappingAssembly.GetExportedTypes());
Configure.AddDeserializedMapping(_mapper.CompileMappingForAllExplicitlyAddedEntities(), "MyWholeDomain");
}
public HbmMapping HbmMapping
{
get { return _mapper.CompileMappingFor(MappingAssembly.GetExportedTypes()); }
}
public IList<HbmMapping> HbmMappings
{
get { return _mapper.CompileMappingForEach(MappingAssembly.GetExportedTypes()).ToList(); }
}
/// <summary>
/// Gets the domain entities.
/// </summary>
/// <returns></returns>
/// <remarks>by default anything that derives from EntityBase and isn't abstract or generic</remarks>
protected virtual IEnumerable<System.Type> GetDomainEntities()
{
List<System.Type> domainEntities = (from t in MappingAssembly.GetExportedTypes()
where typeof(EntityBase<Guid>).IsAssignableFrom(t)
&& (!t.IsGenericType || !t.IsAbstract)
select t
).ToList();
return domainEntities;
}
}
Then in my global.asax Application_Begin event handler I configure it
public class MvcApplication : HttpApplication
{
private const String Sessionkey = "current.session";
private static IWindsorContainer Container { get; set; }
private static ISessionFactory SessionFactory { get; set; }
public static ISession CurrentSession
{
get { return (ISession) HttpContext.Current.Items[Sessionkey]; }
private set { HttpContext.Current.Items[Sessionkey] = value; }
}
protected void Application_Start()
{
Version version = Assembly.GetExecutingAssembly().GetName().Version;
Application["Version"] = String.Format("{0}.{1}", version.Major, version.Minor);
Application["Name"] = ConfigurationManager.AppSettings["ApplicationName"];
//create empty container
//scan this assembly for any installers to register services/components with Windsor
Container = new WindsorContainer().Install(FromAssembly.This());
//API controllers use the dependency resolver and need to be initialized differently than the mvc controllers
GlobalConfiguration.Configuration.DependencyResolver = new WindsorDependencyResolver(Container.Kernel);
//tell ASP.NET to get its controllers from Castle
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container.Kernel));
//initialize NHibernate
ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings[Environment.MachineName];
if (connectionString == null)
throw new ConfigurationErrorsException(String.Format("Connection string {0} is empty.",
Environment.MachineName));
if (String.IsNullOrWhiteSpace(connectionString.ConnectionString))
throw new ConfigurationErrorsException(String.Format("Connection string {0} is empty.",
Environment.MachineName));
string mappingAssemblyName = ConfigurationManager.AppSettings["NHibernate.Mapping.Assembly"];
if (String.IsNullOrWhiteSpace(mappingAssemblyName))
throw new ConfigurationErrorsException(
"NHibernate.Mapping.Assembly key not set in application config file.");
var nh = new NHInit(connectionString.ConnectionString, mappingAssemblyName);
nh.Initialize();
nh.InitializeAudit();
SessionFactory = nh.SessionFactory;
AutoMapConfig.RegisterMaps();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ModelBinderConfig.RegisterModelBinders(ModelBinders.Binders);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
protected void Application_OnEnd()
{
//dispose Castle container and all the stuff it contains
Container.Dispose();
}
protected void Application_BeginRequest() { CurrentSession = SessionFactory.OpenSession(); }
protected void Application_EndRequest()
{
if (CurrentSession != null)
CurrentSession.Dispose();
}
}
}

Autofac. How to get caller class Type?

Suppose we have two classes with same constructor Injectable dependency:
public class FirstClass
{
public FirstClass(ISomeDependency someDependency)
{ }
}
public class SecondClass
{
public SecondClass(ISomeDependency someDependency)
{ }
}
Now we have a registration for ISomeDependency:
builder.Register(x =>
{
string key = GetKeyFromCurrentHttpRequest();
// if "Caller" is "FirstClass" return new Dependency(key);
// else return new Dependency("defaultKey");
}).As<ISomeDependency>();
Note: This is a simplified use case. The real scenario is much more complicated.
1. How to get "Caller" type which tryies to resolve ISomeDependency?
2. Is there a better way design for such situations?
You can use delegate factories do achieve your goal. The only drawback is the FirstClass and SecondClass cannot use ISomeDependency as parameter.
You can try this code in a console application (just add Autofac dependency).
using System;
using Autofac;
namespace test
{
class MainClass
{
public static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder ();
builder.RegisterType<SomeDependency>().As<ISomeDependency>();
builder.RegisterType<FirstClass>();
builder.RegisterType<SecondClass>();
var container = builder.Build();
var dummy = container.Resolve<FirstClass>();
var dummy2 = container.Resolve<SecondClass>();
}
public interface ISomeDependency
{
}
public class SomeDependency : ISomeDependency
{
public delegate ISomeDependency Factory(string value);
private readonly string _value;
public SomeDependency(string value)
{
_value = value;
Console.WriteLine("Value = " + _value);
}
}
public class FirstClass
{
private ISomeDependency _dependency;
public FirstClass(SomeDependency.Factory factory)
{
_dependency = factory.Invoke("my value");
}
}
public class SecondClass
{
private ISomeDependency _dependency;
public SecondClass(SomeDependency.Factory factory)
{
_dependency = factory.Invoke("my value 2");
}
}
}
}

Including a MVC View or string in a javascript bundle using Bundling framework

I'm trying to bundle several javascript files together, but I also need to include a variable from app.config in the js.
My thought was to use a Controller to return a string to set the variable, so going to
~/Javascript/Index would return var foo = "bar"; This works fine.
But when I try to build a bundle, the static files are being included, but the string (or view) isn't showing up. In looking around I found that the Optimizing framework was limited to static files up until 1.1 when support for VirtualPathProviders was implemented.
I upgraded to the latest package, but I can't find any information on how to get a mix of static files and ones generated by a Controller/View to bundle. I guess I just want to use the MVC path provider?
My other thought was to try to use the bundling engine to build a string of the bundled static files and then just append my string and return it all to the browser. But, I can't find a method that allows me to use the bundling engine to return a result of the bundling process.
Integration of dynamic content into the bundling process requires the following steps:
Writing the logic that requests / builds the required content. Generating content from Controller directly requires a bit of work:
public static class ControllerActionHelper
{
public static string RenderControllerActionToString(string virtualPath)
{
HttpContext httpContext = CreateHttpContext(virtualPath);
HttpContextWrapper httpContextWrapper = new HttpContextWrapper(httpContext);
RequestContext httpResponse = new RequestContext()
{
HttpContext = httpContextWrapper,
RouteData = RouteTable.Routes.GetRouteData(httpContextWrapper)
};
// Set HttpContext.Current if RenderActionToString is called outside of a request
if (HttpContext.Current == null)
{
HttpContext.Current = httpContext;
}
IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
IController controller = controllerFactory.CreateController(httpResponse,
httpResponse.RouteData.GetRequiredString("controller"));
controller.Execute(httpResponse);
return httpResponse.HttpContext.Response.Output.ToString();
}
private static HttpContext CreateHttpContext(string virtualPath)
{
HttpRequest httpRequest = new HttpRequest(string.Empty, ToDummyAbsoluteUrl(virtualPath), string.Empty);
HttpResponse httpResponse = new HttpResponse(new StringWriter());
return new HttpContext(httpRequest, httpResponse);
}
private static string ToDummyAbsoluteUrl(string virtualPath)
{
return string.Format("http://dummy.net{0}", VirtualPathUtility.ToAbsolute(virtualPath));
}
}
Implement a virtual path provider that wraps the existing one and intercept all virtual paths that should deliver the dynamic content.
public class ControllerActionVirtualPathProvider : VirtualPathProvider
{
public ControllerActionVirtualPathProvider(VirtualPathProvider virtualPathProvider)
{
// Wrap an existing virtual path provider
VirtualPathProvider = virtualPathProvider;
}
protected VirtualPathProvider VirtualPathProvider { get; set; }
public override string CombineVirtualPaths(string basePath, string relativePath)
{
return VirtualPathProvider.CombineVirtualPaths(basePath, relativePath);
}
public override bool DirectoryExists(string virtualDir)
{
return VirtualPathProvider.DirectoryExists(virtualDir);
}
public override bool FileExists(string virtualPath)
{
if (ControllerActionHelper.IsControllerActionRoute(virtualPath))
{
return true;
}
return VirtualPathProvider.FileExists(virtualPath);
}
public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies,
DateTime utcStart)
{
AggregateCacheDependency aggregateCacheDependency = new AggregateCacheDependency();
List<string> virtualPathDependenciesCopy = virtualPathDependencies.Cast<string>().ToList();
// Create CacheDependencies for our virtual Controller Action paths
foreach (string virtualPathDependency in virtualPathDependenciesCopy.ToList())
{
if (ControllerActionHelper.IsControllerActionRoute(virtualPathDependency))
{
aggregateCacheDependency.Add(new ControllerActionCacheDependency(virtualPathDependency));
virtualPathDependenciesCopy.Remove(virtualPathDependency);
}
}
// Aggregate them with the base cache dependency for virtual file paths
aggregateCacheDependency.Add(VirtualPathProvider.GetCacheDependency(virtualPath, virtualPathDependenciesCopy,
utcStart));
return aggregateCacheDependency;
}
public override string GetCacheKey(string virtualPath)
{
return VirtualPathProvider.GetCacheKey(virtualPath);
}
public override VirtualDirectory GetDirectory(string virtualDir)
{
return VirtualPathProvider.GetDirectory(virtualDir);
}
public override VirtualFile GetFile(string virtualPath)
{
if (ControllerActionHelper.IsControllerActionRoute(virtualPath))
{
return new ControllerActionVirtualFile(virtualPath,
new MemoryStream(Encoding.Default.GetBytes(ControllerActionHelper.RenderControllerActionToString(virtualPath))));
}
return VirtualPathProvider.GetFile(virtualPath);
}
public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
{
return VirtualPathProvider.GetFileHash(virtualPath, virtualPathDependencies);
}
public override object InitializeLifetimeService()
{
return VirtualPathProvider.InitializeLifetimeService();
}
}
public class ControllerActionVirtualFile : VirtualFile
{
public CustomVirtualFile (string virtualPath, Stream stream)
: base(virtualPath)
{
Stream = stream;
}
public Stream Stream { get; private set; }
public override Stream Open()
{
return Stream;
}
}
You also have to implement CacheDependency if you need it:
public class ControllerActionCacheDependency : CacheDependency
{
public ControllerActionCacheDependency(string virtualPath, int actualizationTime = 10000)
{
VirtualPath = virtualPath;
LastContent = GetContentFromControllerAction();
Timer = new Timer(CheckDependencyCallback, this, actualizationTime, actualizationTime);
}
private string LastContent { get; set; }
private Timer Timer { get; set; }
private string VirtualPath { get; set; }
protected override void DependencyDispose()
{
if (Timer != null)
{
Timer.Dispose();
}
base.DependencyDispose();
}
private void CheckDependencyCallback(object sender)
{
if (Monitor.TryEnter(Timer))
{
try
{
string contentFromAction = GetContentFromControllerAction();
if (contentFromAction != LastContent)
{
LastContent = contentFromAction;
NotifyDependencyChanged(sender, EventArgs.Empty);
}
}
finally
{
Monitor.Exit(Timer);
}
}
}
private string GetContentFromControllerAction()
{
return ControllerActionHelper.RenderControllerActionToString(VirtualPath);
}
}
Register your virtual path provider:
public static void RegisterBundles(BundleCollection bundles)
{
// Set the virtual path provider
BundleTable.VirtualPathProvider = new ControllerActionVirtualPathProvider(BundleTable.VirtualPathProvider);
bundles.Add(new Bundle("~/bundle")
.Include("~/Content/static.js")
.Include("~/JavaScript/Route1")
.Include("~/JavaScript/Route2"));
}
Optional: Add Intellisense support to your views. Use <script> tags within your View and let them be removed by a custom ViewResult:
public class DynamicContentViewResult : ViewResult
{
public DynamicContentViewResult()
{
StripTags = false;
}
public string ContentType { get; set; }
public bool StripTags { get; set; }
public string TagName { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (string.IsNullOrEmpty(ViewName))
{
ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result = null;
if (View == null)
{
result = FindView(context);
View = result.View;
}
string viewResult;
using (StringWriter viewContentWriter = new StringWriter())
{
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, viewContentWriter);
View.Render(viewContext, viewContentWriter);
if (result != null)
{
result.ViewEngine.ReleaseView(context, View);
}
viewResult = viewContentWriter.ToString();
// Strip Tags
if (StripTags)
{
string regex = string.Format("<{0}[^>]*>(.*?)</{0}>", TagName);
Match res = Regex.Match(viewResult, regex,
RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Singleline);
if (res.Success && res.Groups.Count > 1)
{
viewResult = res.Groups[1].Value;
}
else
{
throw new InvalidProgramException(
string.Format("Dynamic content produced by View '{0}' expected to be wrapped in '{1}' tag.", ViewName, TagName));
}
}
}
context.HttpContext.Response.ContentType = ContentType;
context.HttpContext.Response.Output.Write(viewResult);
}
}
Use an extension method or add an helper function to your controller:
public static DynamicContentViewResult JavaScriptView(this Controller controller, string viewName, string masterName, object model)
{
if (model != null)
{
controller.ViewData.Model = model;
}
return new DynamicContentViewResult
{
ViewName = viewName,
MasterName = masterName,
ViewData = controller.ViewData,
TempData = controller.TempData,
ViewEngineCollection = controller.ViewEngineCollection,
ContentType = "text/javascript",
TagName = "script",
StripTags = true
};
}
The steps are similiar for other type of dynamic contents. See Bundling and Minification and Embedded Resources for example.
I added a proof of concept repository to GitHub if you want to try it out.

ASP.NET bundling/minification: including dynamically generated Javascript

I have a site that dynamically generates Javascript. The generated code describes type-metadata and some server-side constants so that the clients can easily consume the server's services - so it's very cacheable.
The generated Javascript is served by an ASP.NET MVC controller; so it has a Uri; say ~/MyGeneratedJs.
I'd like to include this Javascript in a Javascript bundle with other static Javascript files (e.g. jQuery etc): so just like static files I want it to be referenced separately in debug mode and in minified form bundled with the other files in non-debug mode.
How can I include dynamically generated Javascript in a bundle?
With VirtualPathProviders this is now possible. Integration of dynamic content into the bundling process requires the following steps:
Writing the logic that requests / builds the required content. Generating content from Controller directly requires a bit of work:
public static class ControllerActionHelper
{
public static string RenderControllerActionToString(string virtualPath)
{
HttpContext httpContext = CreateHttpContext(virtualPath);
HttpContextWrapper httpContextWrapper = new HttpContextWrapper(httpContext);
RequestContext httpResponse = new RequestContext()
{
HttpContext = httpContextWrapper,
RouteData = RouteTable.Routes.GetRouteData(httpContextWrapper)
};
// Set HttpContext.Current if RenderActionToString is called outside of a request
if (HttpContext.Current == null)
{
HttpContext.Current = httpContext;
}
IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
IController controller = controllerFactory.CreateController(httpResponse,
httpResponse.RouteData.GetRequiredString("controller"));
controller.Execute(httpResponse);
return httpResponse.HttpContext.Response.Output.ToString();
}
private static HttpContext CreateHttpContext(string virtualPath)
{
HttpRequest httpRequest = new HttpRequest(string.Empty, ToDummyAbsoluteUrl(virtualPath), string.Empty);
HttpResponse httpResponse = new HttpResponse(new StringWriter());
return new HttpContext(httpRequest, httpResponse);
}
private static string ToDummyAbsoluteUrl(string virtualPath)
{
return string.Format("http://dummy.net{0}", VirtualPathUtility.ToAbsolute(virtualPath));
}
}
Implement a virtual path provider that wraps the existing one and intercept all virtual paths that should deliver the dynamic content.
public class ControllerActionVirtualPathProvider : VirtualPathProvider
{
public ControllerActionVirtualPathProvider(VirtualPathProvider virtualPathProvider)
{
// Wrap an existing virtual path provider
VirtualPathProvider = virtualPathProvider;
}
protected VirtualPathProvider VirtualPathProvider { get; set; }
public override string CombineVirtualPaths(string basePath, string relativePath)
{
return VirtualPathProvider.CombineVirtualPaths(basePath, relativePath);
}
public override bool DirectoryExists(string virtualDir)
{
return VirtualPathProvider.DirectoryExists(virtualDir);
}
public override bool FileExists(string virtualPath)
{
if (ControllerActionHelper.IsControllerActionRoute(virtualPath))
{
return true;
}
return VirtualPathProvider.FileExists(virtualPath);
}
public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies,
DateTime utcStart)
{
AggregateCacheDependency aggregateCacheDependency = new AggregateCacheDependency();
List<string> virtualPathDependenciesCopy = virtualPathDependencies.Cast<string>().ToList();
// Create CacheDependencies for our virtual Controller Action paths
foreach (string virtualPathDependency in virtualPathDependenciesCopy.ToList())
{
if (ControllerActionHelper.IsControllerActionRoute(virtualPathDependency))
{
aggregateCacheDependency.Add(new ControllerActionCacheDependency(virtualPathDependency));
virtualPathDependenciesCopy.Remove(virtualPathDependency);
}
}
// Aggregate them with the base cache dependency for virtual file paths
aggregateCacheDependency.Add(VirtualPathProvider.GetCacheDependency(virtualPath, virtualPathDependenciesCopy,
utcStart));
return aggregateCacheDependency;
}
public override string GetCacheKey(string virtualPath)
{
return VirtualPathProvider.GetCacheKey(virtualPath);
}
public override VirtualDirectory GetDirectory(string virtualDir)
{
return VirtualPathProvider.GetDirectory(virtualDir);
}
public override VirtualFile GetFile(string virtualPath)
{
if (ControllerActionHelper.IsControllerActionRoute(virtualPath))
{
return new ControllerActionVirtualFile(virtualPath,
new MemoryStream(Encoding.Default.GetBytes(ControllerActionHelper.RenderControllerActionToString(virtualPath))));
}
return VirtualPathProvider.GetFile(virtualPath);
}
public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
{
return VirtualPathProvider.GetFileHash(virtualPath, virtualPathDependencies);
}
public override object InitializeLifetimeService()
{
return VirtualPathProvider.InitializeLifetimeService();
}
}
public class ControllerActionVirtualFile : VirtualFile
{
public CustomVirtualFile (string virtualPath, Stream stream)
: base(virtualPath)
{
Stream = stream;
}
public Stream Stream { get; private set; }
public override Stream Open()
{
return Stream;
}
}
You also have to implement CacheDependency if you need it:
public class ControllerActionCacheDependency : CacheDependency
{
public ControllerActionCacheDependency(string virtualPath, int actualizationTime = 10000)
{
VirtualPath = virtualPath;
LastContent = GetContentFromControllerAction();
Timer = new Timer(CheckDependencyCallback, this, actualizationTime, actualizationTime);
}
private string LastContent { get; set; }
private Timer Timer { get; set; }
private string VirtualPath { get; set; }
protected override void DependencyDispose()
{
if (Timer != null)
{
Timer.Dispose();
}
base.DependencyDispose();
}
private void CheckDependencyCallback(object sender)
{
if (Monitor.TryEnter(Timer))
{
try
{
string contentFromAction = GetContentFromControllerAction();
if (contentFromAction != LastContent)
{
LastContent = contentFromAction;
NotifyDependencyChanged(sender, EventArgs.Empty);
}
}
finally
{
Monitor.Exit(Timer);
}
}
}
private string GetContentFromControllerAction()
{
return ControllerActionHelper.RenderControllerActionToString(VirtualPath);
}
}
Register your virtual path provider:
public static void RegisterBundles(BundleCollection bundles)
{
// Set the virtual path provider
BundleTable.VirtualPathProvider = new ControllerActionVirtualPathProvider(BundleTable.VirtualPathProvider);
bundles.Add(new Bundle("~/bundle")
.Include("~/Content/static.js")
.Include("~/JavaScript/Route1")
.Include("~/JavaScript/Route2"));
}
Optional: Add Intellisense support to your views. Use <script> tags within your View and let them be removed by a custom ViewResult:
public class DynamicContentViewResult : ViewResult
{
public DynamicContentViewResult()
{
StripTags = false;
}
public string ContentType { get; set; }
public bool StripTags { get; set; }
public string TagName { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (string.IsNullOrEmpty(ViewName))
{
ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result = null;
if (View == null)
{
result = FindView(context);
View = result.View;
}
string viewResult;
using (StringWriter viewContentWriter = new StringWriter())
{
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, viewContentWriter);
View.Render(viewContext, viewContentWriter);
if (result != null)
{
result.ViewEngine.ReleaseView(context, View);
}
viewResult = viewContentWriter.ToString();
// Strip Tags
if (StripTags)
{
string regex = string.Format("<{0}[^>]*>(.*?)</{0}>", TagName);
Match res = Regex.Match(viewResult, regex,
RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Singleline);
if (res.Success && res.Groups.Count > 1)
{
viewResult = res.Groups[1].Value;
}
else
{
throw new InvalidProgramException(
string.Format("Dynamic content produced by View '{0}' expected to be wrapped in '{1}' tag.", ViewName, TagName));
}
}
}
context.HttpContext.Response.ContentType = ContentType;
context.HttpContext.Response.Output.Write(viewResult);
}
}
Use an extension method or add an helper function to your controller:
public static DynamicContentViewResult JavaScriptView(this Controller controller, string viewName, string masterName, object model)
{
if (model != null)
{
controller.ViewData.Model = model;
}
return new DynamicContentViewResult
{
ViewName = viewName,
MasterName = masterName,
ViewData = controller.ViewData,
TempData = controller.TempData,
ViewEngineCollection = controller.ViewEngineCollection,
ContentType = "text/javascript",
TagName = "script",
StripTags = true
};
}
The steps are similiar for other type of dynamic contents. See Bundling and Minification and Embedded Resources for example.
I added a proof of concept repository to GitHub if you want to try it out.
Darin is right, currently bundling only works on static files. But if you can add a placeholder file with up to date content, bundling does setup file change notifications which will detect automatically when the placeholder file changes.
Also we are going to be moving to using VirtualPathProviders soon which might be a way to serve dynamically generated content.
Update: The 1.1-alpha1 release is out now which has support for VPP
This is not possible. Bundles work only with static files.

StructureMap Question

This is the equivalent of what I'm trying to create with StructureMap:
new ChangePasswordWithNotificationAndLoggingService(
new ChangePasswordService(
new ActiveDirectoryRepository(new ActiveDirectoryCredentials()),
new TokenRepository("")),
new EmailNotificationService(new PasswordChangedNotification(new UserAccount())),
new LoggingService());
This is what I have right now:
ForRequestedType<IChangePasswordService>()
.TheDefault.Is.ConstructedBy(() =>
new ChangePasswordService(DependencyRegistrar.Resolve<IActiveDirectoryRepository>(),
DependencyRegistrar.Resolve<ITokenRepository>()))
.EnrichWith<IChangePasswordService>(x =>
new ChangePasswordWithNotificationAndLoggingService(x,
DependencyRegistrar.Resolve<INotificationService>(),
DependencyRegistrar.Resolve<ILoggingService>()));
I need to pass the UserAccount to the INotificationService...can't figure it out.
I've tried this:
DependencyRegistrar.With(new UserAccount { Username = "test" });
No luck...UserAccount always turns out null. I don't have to do it all with StructureMap, I'm open to any suggestions.
This is what I currently have working:
public static IChangePasswordService ChangePasswordService(UserAccount userAccount)
{
return new ChangePasswordWithNotificationService(
new ChangePasswordService(ActiveDirectoryRepository(), TokenRepository()),
new EmailNotificationService(new PasswordChangedNotification(userAccount)));
}
Have you tried just using AutoWiring? These are all concrete classes with simple construction so StructureMap can figure out what you need.
For<IChangePasswordService>().Use<ChangePasswordService>();
Looking at your construction I think that this simple configuration might just work.
Edit
Regarding the comments.
You should use the With(T instance) method to have the container construct your IChangePasswordService using the given userAccount.
var userAccount = new UserAccount("derans");
var changePasswordService = container.With(userAccount).GetInstance<IChangePasswordService>();
Why not encapsulate the creation of the change password service into a factory - the factory is then an implemented as StructureMap factory that use a UserAccount passed in and the 'ObjectFactory' to create instances of the IIChangePasswordService as required?
I have demo'ed it below:
namespace SMTest
{
class Program
{
static void Main(string[] args)
{
// bootstrapper...
ObjectFactory.Configure(x => x.AddRegistry(new TestRegistry()));
// create factory for use later (IoC manages this)...
var changePasswordServiceFactory = ObjectFactory.GetInstance<IChangePasswordServiceFactory>();
var daveAccount = new UserAccount("Dave Cox");
var steveAccount = new UserAccount("Steve Jones");
var passwordService1 = changePasswordServiceFactory.CreateForUserAccount(daveAccount);
var passwordService2 = changePasswordServiceFactory.CreateForUserAccount(steveAccount);
}
}
public class TestRegistry : Registry
{
public TestRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.AssemblyContainingType(typeof(IChangePasswordService));
x.AssemblyContainingType(typeof(IActiveDirectoryRepository));
x.AssemblyContainingType(typeof(IActiveDirectoryCredentials));
x.AssemblyContainingType(typeof(ITokenRepository));
x.AssemblyContainingType(typeof(INotification));
x.AssemblyContainingType(typeof(INotificationService));
x.AssemblyContainingType(typeof(ILoggingService));
ForRequestedType<ILoggingService>().TheDefault.Is.OfConcreteType<MyLogger>();
ForRequestedType<IActiveDirectoryRepository>().TheDefault.Is.OfConcreteType<MyAdRepository>();
ForRequestedType<IActiveDirectoryCredentials>().TheDefault.Is.OfConcreteType<MyAdCredentials>();
ForRequestedType<ITokenRepository>().TheDefault.Is.OfConcreteType<MyTokenRepository>();
ForRequestedType<IChangePasswordService>().TheDefault.Is.OfConcreteType<ChangePasswordService>();
ForRequestedType<IChangePasswordServiceFactory>().CacheBy(InstanceScope.Singleton).TheDefault.Is.OfConcreteType<StructureMapChangePasswordServiceFactory>();
ForRequestedType<INotification>().TheDefault.Is.OfConcreteType<MyPasswordChangedNotification>();
ForRequestedType<INotificationService>().TheDefault.Is.OfConcreteType<MyEmailNotificationService>();
});
}
}
public interface ILoggingService
{
}
public class MyLogger : ILoggingService
{
}
public class UserAccount
{
public string Name { get; private set; }
public UserAccount(string name)
{
Name = name;
}
}
public interface INotification
{
}
public class MyPasswordChangedNotification : INotification
{
private readonly UserAccount _account;
private readonly ILoggingService _logger;
public MyPasswordChangedNotification(UserAccount account, ILoggingService logger)
{
_account = account;
_logger = logger;
}
}
public interface INotificationService
{
}
public class MyEmailNotificationService : INotificationService
{
private readonly INotification _notification;
private readonly ILoggingService _logger;
public MyEmailNotificationService(INotification notification, ILoggingService logger)
{
_notification = notification;
_logger = logger;
}
}
public interface ITokenRepository
{
}
public class MyTokenRepository : ITokenRepository
{
}
public interface IActiveDirectoryRepository
{
}
public interface IActiveDirectoryCredentials
{
}
public class MyAdCredentials : IActiveDirectoryCredentials
{
}
public class MyAdRepository : IActiveDirectoryRepository
{
private readonly IActiveDirectoryCredentials _credentials;
public MyAdRepository(IActiveDirectoryCredentials credentials)
{
_credentials = credentials;
}
}
public interface IChangePasswordService
{
}
public class ChangePasswordService : IChangePasswordService
{
private readonly IActiveDirectoryRepository _adRepository;
private readonly ITokenRepository _tokenRepository;
private readonly INotificationService _notificationService;
public ChangePasswordService(IActiveDirectoryRepository adRepository, ITokenRepository tokenRepository, INotificationService notificationService)
{
_adRepository = adRepository;
_tokenRepository = tokenRepository;
_notificationService = notificationService;
}
}
public interface IChangePasswordServiceFactory
{
IChangePasswordService CreateForUserAccount(UserAccount account);
}
public class StructureMapChangePasswordServiceFactory : IChangePasswordServiceFactory
{
public IChangePasswordService CreateForUserAccount(UserAccount account)
{
return ObjectFactory.With(account).GetInstance < IChangePasswordService>();
}
}
}

Resources