New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Difficult to use dotless engine in asp.net outside of handler #179

Closed
mwrock opened this Issue Apr 29, 2012 · 7 comments

Comments

Projects
None yet
3 participants
@mwrock
Copy link
Contributor

mwrock commented Apr 29, 2012

First: Excellent library and thanks for your work in providing LESS compilation to the .net community.

I am the project owner of RequestReduce and I use dotless to provide LESS compilation of css that is then minified and bundled. I have my own caching, modules, handlers, config and so I like to call the dotless engine directly. This worked great up to 1.2.4 but I just upgraded to 1.3.2 and now I have to resort to some ugly Reflection in order to get access to the AspNetFactoryContainer. Previously I used new EngineFactory(config).GetEngine() but now if I call this where the config's Web property is true, I get a Pandora exception complaining about no IParameterSource. I see that there is now a way to specify that Parameters should be disabled which looks like it would get around that error, but I do want to use Parameters. So now I use this code:

        if (HttpContext.Current == null)
            engine = new EngineFactory(config).GetEngine();
        else
        {
            var dotLessAssembly = Assembly.GetAssembly(typeof (ContainerFactory));
            var factoryType = dotLessAssembly.GetType("dotless.Core.AspNetContainerFactory");
            var fac = (ContainerFactory)(factoryType.InvokeMember("", BindingFlags.CreateInstance, null, null, null));
            var locator = factoryType.InvokeMember("GetContainer", BindingFlags.InvokeMethod, null, fac, new object[] {config});
            engine =
                (ILessEngine)
                (dotLessAssembly.GetType("Microsoft.Practices.ServiceLocation.IServiceLocator").InvokeMember(
                    "GetInstance", BindingFlags.InvokeMethod, null, locator, new object[] {typeof (ILessEngine)}));
        }

While this works, it is fragile and aesthetically unattractive (unless you are in to that kind of thing). It would be great if your library could make some of this public or a bit more extensible. There are lots of options here and as long as I dont have to take a dependency on Pandora, it would work for me.

Thanks a bunch and thanks for creating dotless!

@lukeapage

This comment has been minimized.

Copy link
Member

lukeapage commented Apr 29, 2012

Why do you have to use reflection? (Its public..?)

We moved asp related things into a separate assembly and it is merged (along with Pandora - you have a dependency on it already as its merged into dotless)

Is there something in particular you want us to change? A suggestion might help us understand the problem.

@mwrock

This comment has been minimized.

Copy link
Contributor Author

mwrock commented Apr 29, 2012

While it is declared public, after the ILMerge, everything in the new assembly is internal. You will probably want to add a internalize file and make sure that your own assemblies don't get internalized. However, this still leaves the ServiceLocator internalized which prevents me from calling GetContainer on the factory. Making that public probably is not a good idea though since many will use that assembly themselves and you dont want to add conflict.

Anyways, I realize this is all rather abstract and I'll try to clarify this in the form of a Pull Request which you may or may not want to accept but it will at least clarify my request. Thanks!

@lukeapage

This comment has been minimized.

Copy link
Member

lukeapage commented Apr 29, 2012

yes, we absolutely do not want aspNet internalised.

If GetEngine accepted a ContainerFactory that would achieve what you wanted?

@mwrock

This comment has been minimized.

Copy link
Contributor Author

mwrock commented Apr 29, 2012

Yes. That would be awesome like a possum.

@lukeapage lukeapage closed this in 2a7b46b Apr 29, 2012

@lukeapage

This comment has been minimized.

Copy link
Member

lukeapage commented Apr 29, 2012

Done!

Will you be desperate for a release or are you ok for a while?

@mwrock

This comment has been minimized.

Copy link
Contributor Author

mwrock commented Apr 29, 2012

Oh I'm fine. I just pushed a maintenance release to Nuget with the reflection code. Whenever you get around to releaseing this, I'll update to that. Thanks alot!

@ForNeVeR

This comment has been minimized.

Copy link

ForNeVeR commented Sep 12, 2016

You know guys what's interesting? I was able to use this API from F# like that:

open dotless.Core
open dotless.Core.configuration
open System.IO

let path = "C:\\Temp\\less"

let config = DotlessConfiguration()
let locator = ContainerFactory().GetContainer(config)
let engine = (locator.GetService (typeof<ILessEngine>)) :?> ILessEngine
engine.CurrentDirectory <- path

let fileName = Path.Combine (path, "main.less")
let content = File.ReadAllText fileName
let result = engine.TransformToCss (content, fileName)

Although an analogous C# program won't compile complaining about inconsistent accessibility:

var config = new DotlessConfiguration();
var cf = new ContainerFactory();
var locator = cf.GetContainer(config); // ← error CS0122: 'ContainerFactory.GetContainer(DotlessConfiguration)' is inaccessible due to its protection level
var engine = (ILessEngine)locator.GetService(typeof(ILessEngine));
engine.CurrentDirectory = path;

That's how I found that issue. Probably that's not a good idea to reference internal types (no matter of what kind) in a public API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment