Exemplo n.º 1
0
        /// <summary>
        /// Compile and register dyncode app api controller (for new or updated app api).
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public async ValueTask <bool> PrepareController(RouteValueDictionary values)
        {
            var wrapLog = Log.Call <bool>();

            var apiFile = (string)values["apiFile"];
            var dllName = (string)values["dllName"];

            // If we have a key (that controller is compiled and registered, but not updated) controller was prepared before, so just return values.
            // Alternatively remove older version of AppApi controller (if we got updated flag from file system watcher).
            if (_compiledAppApiControllers.TryGetValue(apiFile, out var updated))
            {
                Log.Add($"_compiledAppApiControllers have value: {updated} for: {apiFile}.");
                if (updated)
                {
                    RemoveController(dllName, apiFile);
                }
                else
                {
                    return(wrapLog(
                               $"ok, nothing to do, AppApi Controller is already compiled and added to ApplicationPart: {apiFile}.",
                               true));
                }
            }

            Log.Add($"We need to prepare controller for: {apiFile}.");

            // Check for AppApi file
            if (!File.Exists(apiFile))
            {
                return(wrapLog($"Error, missing AppApi file {apiFile}.", false));
            }

            // note: this may look like something you could optimize/cache the result, but that's a bad idea
            // because when the file changes, the type-object will be different, so please don't optimize :)

            // Check for AppApi source code
            var apiCode = await File.ReadAllTextAsync(apiFile);

            if (string.IsNullOrWhiteSpace(apiCode))
            {
                return(wrapLog($"Error, missing AppApi code in file {apiFile}.", false));
            }

            // Build new AppApi Controller
            Log.Add($"Compile assembly: {apiFile}, {dllName}");
            var assembly = new Compiler().Compile(apiFile, dllName);

            // Add new key to concurrent dictionary, before registering new AppAPi controller.
            if (!_compiledAppApiControllers.TryAdd(apiFile, false))
            {
                return(wrapLog($"Error, while adding key {apiFile} to concurrent dictionary, so will not register AppApi Controller to avoid duplicate controller routes.", false));
            }

            // Register new AppApi Controller.
            AddController(dllName, assembly);

            return(wrapLog($"ok, Controller is compiled and added to ApplicationParts: {apiFile}.", true));
        }
        public override async ValueTask <RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
        {
            var wrapLog = Log.Call <RouteValueDictionary>();

            // Check required route values: alias, appFolder, controller, action.
            if (!values.ContainsKey("alias"))
            {
                return(wrapLog("Error: missing required 'alias' route value", values));
            }
            var aliasId = int.Parse((string)values["alias"]);

            if (!values.ContainsKey("appFolder"))
            {
                return(wrapLog("Error: missing required 'appFolder' route value", values));
            }
            var appFolder = (string)values["appFolder"];

            if (!values.ContainsKey("controller"))
            {
                return(wrapLog("Error: missing required 'controller' route value", values));
            }
            var controller = (string)values["controller"];

            if (!values.ContainsKey("action"))
            {
                return(wrapLog("Error: missing required 'action' route value", values));
            }
            var action = (string)values["action"];

            Log.Add($"TransformAsync route required values are present, alias:{aliasId}, app:{appFolder}, ctrl:{controller}, act:{action}.");

            try
            {
                var controllerTypeName = $"{controller}Controller";
                Log.Add($"Controller TypeName: {controllerTypeName}");
                values.Add("controllerTypeName", controllerTypeName);

                var edition = GetEdition(values);
                Log.Add($"Edition: {edition}");

                var alias     = _tenantResolver.GetAlias();
                var aliasPart = $@"Content\Tenants\{alias.TenantId}\Sites\{alias.SiteId}\2sxc";

                var controllerFolder = Path.Combine(aliasPart, appFolder, edition.Backslash(), "api");
                Log.Add($"Controller Folder: {controllerFolder}");

                var area = $"{alias.SiteId}/{OqtConstants.ApiAppLinkPart}/{appFolder}/{edition}api";
                Log.Add($"Area: {area}");
                values.Add("area", area);

                var controllerPath = Path.Combine(controllerFolder, controllerTypeName + ".cs");
                Log.Add($"Controller Path: {controllerPath}");

                var apiFile = Path.Combine(_hostingEnvironment.ContentRootPath, controllerPath);
                Log.Add($"Absolute Path: {apiFile}");
                values.Add("apiFile", apiFile);

                var dllName = $"DynCode_{controllerFolder.Replace(@"\", "_")}_{System.IO.Path.GetFileNameWithoutExtension(apiFile)}";
                Log.Add($"Dll Name: {dllName}");
                values.Add("dllName", dllName);

                // help with path resolution for compilers running inside the created controller
                httpContext.Request?.HttpContext.Items.Add(CodeCompiler.SharedCodeRootPathKeyInCache, controllerFolder);

                return(wrapLog($"ok, TransformAsync route required values are prepared", values));
            }
            catch (Exception e)
            {
                return(wrapLog($"Error, unexpected error {e.Message} while preparing controller.", values));
            }
        }