U4-6865 - not possible to change FileService root directory when running outside webcontext

Created by Per Ploug 23 Jul 2015, 08:31:38 Updated by Shannon Deminick 23 Jul 2015, 16:35:27

If you run Umbraco outside the webcontext, in for.ex a console app, Umbraco will not use the right root path.

For instance, if you run c:\console\console.exe against an umbraco site in c:\umbraco - the fileservice will use c:\console as root dir when looking up files - which is not correct, it should use c:\umbraco for this

The core of the issue, is that FileServices passes to the TemplateRepository is using a virtual path (~/views) to set the FileSystem rootPath and setting this rootPath is done with IOHelper.MapPath() which end up using Assembly.GetExecutingAssembly() to find the root.


Shannon Deminick 23 Jul 2015, 16:35:27

There's two ways to do this, one is the 'public' way which I've updated the core to support:

In this revision: 8c871a617261ea9a303fc7e7c066830f01e318f0

I've publicized a ctor for the ServiceContext that allows you to pass in an instance of the RepositoryFactory. Each file based repository (i.e. Templates, stylesheets, etc...) get's an IFileSystem instance passed to their ctor. Each method on the RepositoryFactory is virtual so you can override them.

In this revision: 37076fa4eb27fa59d6176e928269234430a738fc

I've changed how the CoreBootManager works a little bit. With this change, you can now override CreateServiceContext which you can of course return your own custom service context.

Therefore, in your BootManager (i.e. ConsoleBootManager), you can override CreateServiceContext and return a custom ServiceContext. The default would look like:

        protected override ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory)
            return new ServiceContext(
                new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
                new PetaPocoUnitOfWorkProvider(dbFactory),
                new FileUnitOfWorkProvider(),
                new PublishingStrategy(),

So what you'd need to do is create a custom implementation of RepositoryFactory and override methods such as CreateTemplateRepository, which by default looks like:

        public override ITemplateRepository CreateTemplateRepository(IDatabaseUnitOfWork uow)
            return new TemplateRepository(uow, 
                _logger, _sqlSyntax,
                new PhysicalFileSystem(SystemDirectories.Masterpages),
                new PhysicalFileSystem(SystemDirectories.MvcViews),

but you could pass in your own PhysicalFileSystem root paths.

Second is the hacking way which is far simpler but requires reflection:

The way the root path is determined is based on the IOHelper. You can use reflection to set it's root path, this is it's method:

        /// <summary>
        /// Allows you to overwrite RootDirectory, which would otherwise be resolved
        /// automatically upon application start.
        /// </summary>
        /// <remarks>The supplied path should be the absolute path to the root of the umbraco site.</remarks>
        /// <param name="rootPath"></param>
        internal static void SetRootDirectory(string rootPath)
            _rootDir = rootPath;

So you can set that to whatever you want and since by default the RepositoryFactory uses IOHelper to resolve paths, that should work too.

Priority: Normal

Type: Bug

State: Fixed

Assignee: Shannon Deminick

Difficulty: Normal


Backwards Compatible: True

Fix Submitted:

Affected versions:

Due in version: 7.3.0


Story Points: