static void Main(string[] args) { // parse this command line: /* fluent HumanResources.dll --types Person,Employee -f HumanResources.Fluent.dll * --overwrite --prefix Magic --suffix Cloned --property-name Target */ var app = new CommandLineApplication { Name = "fluent", Description = "Fluent API Assembly Generator" }; app.HelpOption("-?|-h|--help"); var asmArg = app.Argument("assembly", "The fully-qualified path to the source assembly to scan."); var scanTypesOption = app.Option("-t|--types <types>", "The types for which to generate fluent APIs.", CommandOptionType.SingleValue); var targetAsmFileOption = app.Option("-f|--file <file>", "The file name of the target output assembly to generate.", CommandOptionType.SingleValue); var overwriteOption = app.Option("-d|--overwrite", "Instructs to delete any previously-built existing assembly.", CommandOptionType.NoValue); var prefixOption = app.Option("-b|--prefix <prefix>", "The string to prepend to an output type.", CommandOptionType.SingleValue); var suffixOption = app.Option("-e|--suffix <suffix>", "The string to append to an output type.", CommandOptionType.SingleValue); var propertyNameOption = app.Option("-p|--property-name <property>", "The name of the property that returns a reference to the underlying type.", CommandOptionType.SingleValue); app.OnExecute(() => { var console = PowerConsole.SmartConsole.Default; var sourceAsmPath = asmArg.Value; if (string.IsNullOrWhiteSpace(sourceAsmPath)) { console.WriteError("You must specify the fully-qualified path to the assembly to scan."); return(1); } try { Assembly asm; try { asm = Assembly.LoadFile(sourceAsmPath); } catch (FileNotFoundException) { console.WriteError($"The system cannot find the specified assembly:\n{sourceAsmPath}\n"); return(3); } var scanTypes = scanTypesOption.HasValue() ? scanTypesOption.Value().Split(',') : null; var typeList = new List <Type>(); if (scanTypes?.Length > 0) { typeList.AddRange(asm.GetTypes().Where(t => scanTypes.Contains(t.Name))); } using (var config = FluentApiFactory.Configure(overwriteOption.HasValue())) { config .OnError(error => Console.WriteLine($"A critical error occured: {error}")) .OnDeleteError((error, builder, file) => Console.WriteLine($"Could not delete the file '{file}'. Reason for failure: {error.Message}")) .WithOptions() .SetFluentTypeNamePrefix(prefixOption.HasValue() ? prefixOption.Value() : "Fluent") .SetProxyClassNameSuffix(suffixOption.HasValue() ? suffixOption.Value() : "Proxy") .SetWrappedObjectPropertyName(propertyNameOption.HasValue() ? propertyNameOption.Value() : "Object") .WithConfig(); if (typeList.Count > 0) { config.Scan(typeList.ToArray()); } else { config.ScanAssembly(asm, false); } var fileName = targetAsmFileOption.Value(); var dirName = string.IsNullOrWhiteSpace(fileName) ? Environment.CurrentDirectory : Path.GetDirectoryName(fileName); if (string.IsNullOrWhiteSpace(dirName)) { dirName = Path.GetDirectoryName(sourceAsmPath) ?? Environment.CurrentDirectory; } // build the assembly and get the result var result = config.Build(fileName).Result(); if (true == result?.Succeeded) { console.WriteSuccess("Success!\n") .WriteLine("The generated assembly is:") .WriteLine(Path.Combine(dirName, result.AssemblyFileName)); } else { console.WriteError($"Could not create the assembly! {result?.Error?.Message}"); } } return(0); } catch (Exception ex) { console.WriteError(ex); } return(2); }); app.Execute(args); }
public static void Build() { var console = SmartConsole.Default; try { // generate a dynamic assembly for the single Person class (very unlikely) // minimalistic approach: var result = FluentApiFactory.Configure(overwrite: true) .Scan(typeof(Person), typeof(Employee)) .Build() .ReleaseResources() .Result(); WriteAssemblyLocation(result); /* * // or if you want to scan the whole HumanResources assembly * // you have to be explicit; the ScanAssembly(Assembly) and * // ScanAssemblyFrom(Type) methods retrieve only types marked * // with the custom attribute FluentApiTargetAttribute * var types = typeof(Person).Assembly.GetTypes(); * * result = FluentApiFactory.Configure(overwrite: true).Scan(types).Build().ReleaseResources().Result(); * * WriteAssemblyLocation(result); * * // you can even generate multiple dynamic assemblies * // with this full-blown approach: * using (var config = FluentApiFactory.Configure(true)) * { * FluentApiFactoryExecutionResult result1 = null; * result = config * .OnError(error => console.WriteLine($"A critical error occured: {error}")) * .OnDeleteError((error, builder, file) => * console.WriteLine($"Could not delete the file '{file}'. Reason for failure: {error.Message}") * ) * .WithOverwriteOptions() * // these 'Set...' methods modify the default prefix and suffix values * // public interface IPerson {...} * // internal sealed class PersonCloned : IPerson {...} * .SetProxyClassNameSuffix("Cloned") * .SetFluentTypeNamePrefix("Magic") * // public class MagicPerson { ... public virtual IPerson Target { get; } } * .SetWrappedObjectPropertyName("Target") * .WithConfig() * .ScanAssemblyFrom(typeof(Person)) * .Build() * .Result(); * * WriteAssemblyLocation(result); * WriteAssemblyLocation(result1); * } * * if (result.Succeeded) * { * console.WriteLines("What's next? Grab that file and a reference to it in your project.", * "You'll be able to use your fluent wrapper as shown in the next demo.", * "The assemblies' names are similar to: HumanResources.DynamicFluentApis.abcdef.dll ", * "and HumanResources.Web.Mvc.DynamicFluentApis.fedcba.dll", * "Where 'abcdef' and 'fedcba' are (for instance) the hash codes generated ", * "for the dynamic fluent API assemblies."); * } */ } catch (Exception ex) { console.WriteError($"An unexpected error occured: {ex.Message}"); } void WriteAssemblyLocation(FluentApiFactoryExecutionResult result) { if (true == result?.Succeeded) { console.WriteLine($"The generated assembly is {Environment.CurrentDirectory}\\{result.AssemblyFileName}!"); } else { console.WriteError("Could not create the assembly! " + result?.Error?.Message); } } }