コード例 #1
0
        protected override void assembleTypes(GeneratedAssembly assembly, StoreOptions options)
        {
            assembly.Rules.ReferenceTypes(GetType());
            assembly.ReferenceAssembly(GetType().Assembly);
            assembly.ReferenceAssembly(typeof(T).Assembly);


            assembly.Rules.ReferenceTypes(_applyMethods.ReferencedTypes().ToArray());
            assembly.Rules.ReferenceTypes(_createMethods.ReferencedTypes().ToArray());
            assembly.Rules.ReferenceTypes(_shouldDeleteMethods.ReferencedTypes().ToArray());

            // Walk the assembly dependencies for the projection and aggregate types,
            // and this will catch generic type argument dependencies as well. For GH-2061
            assembly.Rules.ReferenceTypes(GetType(), typeof(T));

            assembly.UsingNamespaces.Add("System");
            assembly.UsingNamespaces.Add("System.Linq");

            _isAsync = _createMethods.IsAsync || _applyMethods.IsAsync;

            _aggregateMapping = options.Storage.FindMapping(typeof(T));


            if (_aggregateMapping.IdMember == null)
            {
                throw new InvalidDocumentException(
                          $"No identity property or field can be determined for the aggregate '{typeof(T).FullNameInCode()}', but one is required to be used as an aggregate in projections");
            }


            buildLiveAggregationType(assembly);
            buildInlineAggregationType(assembly);
        }
コード例 #2
0
        public static (EventDocumentStorage, string) GenerateStorage(StoreOptions options)
        {
            var assembly = new GeneratedAssembly(new GenerationRules(SchemaConstants.MartenGeneratedNamespace));

            assembly.ReferenceAssembly(typeof(EventGraph).Assembly);

            var builderType = assembly.AddType(EventDocumentStorageTypeName, typeof(EventDocumentStorage));

            buildSelectorMethods(options, builderType);

            var appendType = buildAppendEventOperation(options.EventGraph, assembly);

            builderType.MethodFor(nameof(EventDocumentStorage.AppendEvent))
            .Frames.Code($"return new Marten.Generated.AppendEventOperation(stream, e);");

            var insertType = buildInsertStream(builderType, assembly, options.EventGraph);

            var streamQueryHandlerType = buildStreamQueryHandlerType(options.EventGraph, assembly);

            buildQueryForStreamMethod(options.EventGraph, builderType);

            var updateType = buildUpdateStreamVersion(builderType, assembly, options.EventGraph);


            var compiler = new AssemblyGenerator();

            compiler.ReferenceAssembly(typeof(IMartenSession).Assembly);
            compiler.Compile(assembly);


            var writer = new StringWriter();

            writer.WriteLine($"    // {streamQueryHandlerType.TypeName}");
            writer.WriteLine(streamQueryHandlerType.SourceCode);
            writer.WriteLine();

            writer.WriteLine($"    // {insertType.TypeName}");
            writer.WriteLine(insertType.SourceCode);
            writer.WriteLine();

            writer.WriteLine($"    // {appendType.TypeName}");
            writer.WriteLine(appendType.SourceCode);
            writer.WriteLine();

            writer.WriteLine($"    // {updateType.TypeName}");
            writer.WriteLine(updateType.SourceCode);
            writer.WriteLine();

            writer.WriteLine($"    // {builderType.TypeName}");
            writer.WriteLine(builderType.SourceCode);
            writer.WriteLine();



            var code = writer.ToString();

            var storage = (EventDocumentStorage)Activator.CreateInstance(builderType.CompiledType, options);

            return(storage, code);
        }
コード例 #3
0
        public void GenerateResolver(GeneratedAssembly generatedAssembly)
        {
            if (_resolverType != null)
            {
                return;                        // got some kind of loop in here we need to short circuit
            }
            if (ErrorMessages.Any() || Dependencies.SelectMany(x => x.ErrorMessages).Any())
            {
                return;
            }

            var typeName = (ServiceType.FullNameInCode() + "_" + Name).Sanitize();


            var buildType = ServiceType.MustBeBuiltWithFunc() || ImplementationType.MustBeBuiltWithFunc()
                ? typeof(object)
                : ServiceType;

            _resolverType = generatedAssembly.AddType(typeName, ResolverBaseType.MakeGenericType(buildType));

            foreach (var relatedAssembly in relatedAssemblies())
            {
                generatedAssembly.ReferenceAssembly(relatedAssembly);
            }

            var method = _resolverType.MethodFor("Build");

            var frame = CreateBuildFrame();

            method.Frames.Add(frame);
        }
コード例 #4
0
        public static EventDocumentStorage GenerateStorage(StoreOptions options)
        {
            var assembly = new GeneratedAssembly(new GenerationRules("Marten.Generated"));

            assembly.ReferenceAssembly(typeof(EventGraph).Assembly);

            var builderType = assembly.AddType("GeneratedEventDocumentStorage", typeof(EventDocumentStorage));

            buildSelectorMethods(options, builderType);

            buildAppendEventOperation(options.Events, assembly);

            builderType.MethodFor(nameof(EventDocumentStorage.AppendEvent))
            .Frames.Code($"return new Marten.Generated.AppendEventOperation(stream, e);");

            buildInsertStream(builderType, assembly, options.Events);

            var streamQueryHandlerType = buildStreamQueryHandlerType(options.Events, assembly);

            buildQueryForStreamMethod(options.Events, builderType);

            buildUpdateStreamVersion(builderType, assembly, options.Events);


            var compiler = new AssemblyGenerator();

            compiler.ReferenceAssembly(typeof(IMartenSession).Assembly);
            compiler.Compile(assembly);

            var code = streamQueryHandlerType.SourceCode;

            return((EventDocumentStorage)Activator.CreateInstance(builderType.CompiledType, options));
        }
コード例 #5
0
        /// <summary>
        /// Generate a new method for the basic base class. The base class "TObject" should
        /// only have a single declared method
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="rules"></param>
        /// <typeparam name="TObject"></typeparam>
        /// <returns></returns>
        private static CodegenResult <TObject> ForBaseOf <TObject>(Action <GeneratedType, GeneratedMethod> configuration)
        {
            if (typeof(TObject).GetMethods().Length != 1)
            {
                throw new ArgumentOutOfRangeException(nameof(TObject), "The supplied base type or interface can only have exactly one declared method");
            }

            var rules    = Builder.Rules();
            var assembly = new GeneratedAssembly(rules);

            var generatedType = assembly.AddType("Tests", "GeneratedType", typeof(TObject));

            var method = generatedType.Methods.Single();

            configuration(generatedType, method);

            if (typeof(TObject).IsGenericType)
            {
                foreach (var genericTypeArgument in typeof(TObject).GenericTypeArguments)
                {
                    assembly.ReferenceAssembly(genericTypeArgument.Assembly);
                }
            }

            assembly.CompileAll(new InMemoryOnlyCompileStrategy(new NullLogger <InMemoryOnlyCompileStrategy>()));

            return(new CodegenResult <TObject>(generatedType.CreateInstance <TObject>(), generatedType.SourceCode));
        }
コード例 #6
0
        public static GeneratedType AssembleTypes(StoreOptions options, GeneratedAssembly assembly)
        {
            assembly.ReferenceAssembly(typeof(EventGraph).Assembly);

            var builderType = assembly.AddType(EventDocumentStorageTypeName, typeof(EventDocumentStorage));

            buildSelectorMethods(options, builderType);

            buildAppendEventOperation(options.EventGraph, assembly);

            builderType.MethodFor(nameof(EventDocumentStorage.AppendEvent))
            .Frames.Code($"return new Marten.Generated.AppendEventOperation(stream, e);");

            buildInsertStream(builderType, assembly, options.EventGraph);

            buildStreamQueryHandlerType(options.EventGraph, assembly);

            buildQueryForStreamMethod(options.EventGraph, builderType);

            buildUpdateStreamVersion(builderType, assembly, options.EventGraph);
            return(builderType);
        }
コード例 #7
0
        /// <summary>
        /// Given the specified <see cref="BlueprintApiOptions" /> will generate and compile an
        /// <see cref="IApiOperationExecutor" /> that can be used to execute any operation that
        /// has been identified by the model of the options passed.
        /// </summary>
        /// <param name="options">The configured options.</param>
        /// <param name="serviceProvider">The configured service provider.</param>
        /// <returns>An executor built from the given options and data model.</returns>
        public CodeGennedExecutor Build(BlueprintApiOptions options, IServiceProvider serviceProvider)
        {
            var model = options.Model;

            // We have multiple ways in which we work with generated assemblies, depending on context:
            //
            //  - We are writing unit tests which create many pipelines (within Blueprint itself). Here we
            //    would want to use in-memory compilation and assembly loading only
            //
            //  - We have deployed an app using generated code. We want to use pre-compiled DLLs loaded as
            //    part of the usual loading process. This is done by creating an assembly and PDB that is
            //    deployed with the application and loaded below (see step 1)
            //
            //  - We are in development. Here we wish to generate and load a new DLL on application startup and
            //    store in the temp folder of the machine. This means the DLL is _not_ loaded as normal part
            //    of .NET process and therefore we can (re)create at will on startup without worrying about
            //    the existence of an existing DLL

            // 1. Try and find an already loaded assembly
            foreach (var loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (loadedAssembly.GetName().Name == options.GenerationRules.AssemblyName)
                {
                    // The assembly exists in the current domain, therefore it has either already been generated in this
                    // process OR it has previously been compiled and loaded as part of normal assembly loading (pre-compiled
                    // as part of dotnet publish)

                    this._logger.LogInformation("Assembly {AssemblyName} already exists, using to create executor.", options.GenerationRules.AssemblyName);

                    return(CreateFromAssembly(options, serviceProvider, model, loadedAssembly));
                }
            }

            // 2. Do we have the DLL stored alongside this application but NOT loaded?
            var directory    = Path.GetDirectoryName(typeof(ApiOperationExecutorBuilder).Assembly.Location);
            var assemblyPath = Path.Combine(directory, options.GenerationRules.AssemblyName) + ".dll";

            if (File.Exists(assemblyPath))
            {
                this._logger.LogInformation(
                    "Assembly {AssemblyName} found at {AssemblyLocation}. Loading and using to create executor.",
                    options.GenerationRules.AssemblyName,
                    assemblyPath);

                var loadedPipelineDll = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);

                return(CreateFromAssembly(options, serviceProvider, model, loadedPipelineDll));
            }

            // 2. We DO NOT have any existing DLLs. In that case we are going to generate the source code using our configured
            // middlewares and then hand off to AssemblyGenerator to compile and load the assembly (which may be in-memory, stored
            // to a temp folder or stored to the project output folder)
            this._logger.LogInformation("Building Blueprint API operation executor for {0} operations", options.Model.Operations.Count());

            using var serviceScope = serviceProvider.CreateScope();

            foreach (var middleware in options.MiddlewareBuilders)
            {
                this.Use(middleware);
            }

            var typeToCreationMappings = new Dictionary <Type, Func <Type> >();

            // Start the definition for a new generated assembly
            var assembly = new GeneratedAssembly(options.GenerationRules);

            foreach (var operation in model.Operations)
            {
                this._references.Add(operation.OperationType.Assembly);
            }

            foreach (var a in this._references)
            {
                this._logger.LogDebug("Referencing assembly {0}", a.FullName);

                assembly.ReferenceAssembly(a);
            }

            foreach (var operation in model.Operations)
            {
                this._logger.LogDebug("Generating executor for {0}", operation.OperationType.FullName);

                var typeName = NormaliseTypeName(operation);

                var pipelineExecutorType = assembly.AddType(
                    operation.OperationType.Namespace,
                    typeName,
                    typeof(IOperationExecutorPipeline));

                // We need to set up a LoggerVariable once, to be shared between methods
                pipelineExecutorType.AllInjectedFields.Add(new LoggerVariable(typeName));

                var executeMethod       = pipelineExecutorType.MethodFor(nameof(IOperationExecutorPipeline.ExecuteAsync));
                var executeNestedMethod = pipelineExecutorType.MethodFor(nameof(IOperationExecutorPipeline.ExecuteNestedAsync));

                this.Generate(options, serviceProvider, executeMethod, operation, model, serviceScope, false);
                this.Generate(options, serviceProvider, executeNestedMethod, operation, model, serviceScope, true);

                typeToCreationMappings.Add(
                    operation.OperationType,
                    () => pipelineExecutorType.CompiledType);
            }

            this._logger.LogInformation("Compiling {0} pipeline executors", typeToCreationMappings.Count);
            assembly.CompileAll(serviceProvider.GetRequiredService <ICompileStrategy>());
            this._logger.LogInformation("Done compiling {0} pipeline executors", typeToCreationMappings.Count);

            return(new CodeGennedExecutor(
                       serviceProvider,
                       model,
                       assembly,
                       typeToCreationMappings));
        }