项目作者: bigabdoul

项目描述 :
Provides functionalities that allow building a dynamic assembly with fluent API support for types from another assembly.
高级语言: C#
项目地址: git://github.com/bigabdoul/dynamic-fluent-apis.git
创建时间: 2019-05-16T20:48:03Z
项目社区:https://github.com/bigabdoul/dynamic-fluent-apis

开源协议:MIT License

下载


dynamic-fluent-apis

DynamicFluentApis is a .NET (Framework 4.6.1) project written in C# which provides functionalities that allow you to build dynamic assemblies with fluent API support, for types located in other assemblies. Simply put, never, ever write that kind of code again:

  1. using System;
  2. namespace Demo1.LegacyWayOfProvidingFluentApiSupport
  3. {
  4. public class Program
  5. {
  6. public static void Main()
  7. {
  8. var p = new PersonWrapper()
  9. .FirstName("Abdoul")
  10. .LastName("Kaba")
  11. .BirthDate(new DateTime(1990,7,29))
  12. .Object;
  13. Console.Write($"Hello, {p.FirstName}! You say you are {p.Age} ");
  14. Console.WriteLine("and your last name is {p.LastName}, right?");
  15. }
  16. public interface IPerson
  17. {
  18. string FirstName { get; set; }
  19. string LastName { get; set; }
  20. DateTime BirthDate { get; set; }
  21. double Age { get; }
  22. }
  23. public class Person : IPerson
  24. {
  25. public virtual string FirstName { get; set; }
  26. public virtual string LastName { get; set; }
  27. public virtual DateTime BirthDate { get; set; }
  28. public double Age { get => DateTime.Today.Subtract(BirthDate).TotalDays / 365; }
  29. }
  30. public class PersonWrapper
  31. {
  32. IPerson _person;
  33. public PersonWrapper()
  34. {
  35. _person = new Person();
  36. }
  37. public PersonWrapper(IPerson person)
  38. {
  39. _person = person;
  40. }
  41. public string FirstName() => _person.FirstName;
  42. public PersonWrapper FirstName(string value)
  43. {
  44. _person.FirstName = value;
  45. return this;
  46. }
  47. public string LastName() => _person.LastName;
  48. public PersonWrapper LastName(string value)
  49. {
  50. _person.LastName = value;
  51. return this;
  52. }
  53. public DateTime BirthDate() => _person.BirthDate;
  54. public PersonWrapper BirthDate(DateTime value)
  55. {
  56. _person.BirthDate = value;
  57. return this;
  58. }
  59. public IPerson Object { get => _person; }
  60. }
  61. }
  62. }

What’s the problem with the code above? Well, technically not much! But, for every single type you want to be fluent API capable, you need to manually write a wrapper class like PersonWrapper just to be able to chain method calls. This can be tedious when you have an assembly with dozens or even hundreds of interesting types you want to support.

That’s where DynamicFluentApis steps in and generates all that boiler-plate code for you. So, instead of writing code like the one above, you just let DynamicFluentApis generate another dynamic assembly for you that supports fluent API on whatever types you want. You then just grab that assembly and add it as a reference to the latest hot project you’re working on.

Steps to generate a dynamic assembly with fluent API support

First things first.

  1. From your favourite command line or terminal, change the working directory to one of your liking and then pull this repo with git (or download a zipped version): > git clone https://github.com/bigabdoul/dynamic-fluent-apis.git
  2. Reference both projects, namely DynamicFluentApis and DynamicFluentApis.Core, within a new console or a unit test project (using .NET Framework 4.6.1).
  3. Add a reference to the console project from the library/libraries or project(s) you want to provide fluent API support with. Let’s call these referenced assemblies HumanResources and HumanResources.Web.Mvc for instance.
  4. Pretending that HumanResources contains the Person class, and HumanResources.Web.Mvc contains the BootstrapModalModel class, write the following code to get started:
  1. using System;
  2. using static System.Console;
  3. using DynamicFluentApis;
  4. using DynamicFluentApis.Core;
  5. using HumanResources;
  6. using HumanResources.Web.Mvc;
  7. namespace Demo2.GeneratingDynamicAssemblies
  8. {
  9. public class Program
  10. {
  11. public static void Main()
  12. {
  13. try
  14. {
  15. // generate a dynamic assembly for the single Person class (very unlikely)
  16. // minimalistic approach:
  17. var result = FluentApiFactory.Configure().Scan(typeof(Person)).Build().ReleaseResources().Result();
  18. WriteAssemblyLocation(result);
  19. // or if you want to scan the whole HumanResources assembly
  20. // you have to be explicit; the ScanAssembly(Assembly) and
  21. // ScanAssemblyFrom(Type) methods retrieve only types marked
  22. // with the custom attribute FluentApiTargetAttribute
  23. var types = typeof(Person).Assembly.GetTypes();
  24. result = FluentApiFactory.Configure(overwrite: true).Scan(types).Build().ReleaseResources().Result();
  25. WriteAssemblyLocation(result);
  26. // you can even generate multiple dynamic assemblies
  27. // with this full-blown approach:
  28. using (var config = FluentApiFactory.Configure(true))
  29. {
  30. FluentApiFactoryExecutionResult result1 = null;
  31. result = config
  32. .OnError(error => WriteLine($"A critical error occured: {error}"))
  33. .OnDeleteError((error, builder, file) =>
  34. WriteLine($"Could not delete the file '{file}'. Reason for failure: {error.Message}")
  35. )
  36. .WithOverwriteOptions()
  37. // these 'Set...' methods modify the default prefix and suffix values
  38. // public interface IPerson {...}
  39. // internal sealed class PersonCloned : IPerson {...}
  40. .SetProxyClassNameSuffix("Cloned")
  41. .SetFluentTypeNamePrefix("Magic")
  42. // public class MagicPerson { ... public virtual IPerson Target { get; } }
  43. .SetWrappedObjectPropertyName("Target")
  44. .WithConfig()
  45. .ScanAssemblyFrom(typeof(Person))
  46. .Build()
  47. .SetResult(r => result1 = r)
  48. .Reset()
  49. // default options use 'Proxy' suffix (the above would have been
  50. // 'PersonProxy' instead of 'PersonCloned'),
  51. // and 'Fluent' prefix ('FluentPerson' instead of 'MagicPerson')
  52. .WithDefaultOptions(overwrite: true)
  53. .ScanAssemblyFrom(typeof(BootstrapModalModel))
  54. // will generate, amongst others, the following types:
  55. // public interface IBootstrapModalModel {...}
  56. // internal sealed class BootstrapModalModelProxy : IBootstrapModalModel {...}
  57. // public class FluentBootstrapModalModel {...}
  58. .Build()
  59. .Result();
  60. WriteAssemblyLocation(result);
  61. WriteAssemblyLocation(result1);
  62. }
  63. if (result.Succeeded)
  64. {
  65. WriteLine("What's next? Grab that file and a reference to it in your project.");
  66. WriteLine("You'll be able to use your fluent wrapper as shown in the next demo.");
  67. Write("The assemblies' names are similar to: HumanResources.DynamicFluentApis.abcdef.dll ");
  68. WriteLine("and HumanResources.Web.Mvc.DynamicFluentApis.fedcba.dll");
  69. Write("Where 'abcdef' and 'fedcba' are (for instance) the hash codes generated ");
  70. WriteLine("for the dynamic fluent API assemblies.");
  71. }
  72. }
  73. catch(Exception ex)
  74. {
  75. WriteLine($"An unexpected error occured: {ex.Message}");
  76. }
  77. void WriteAssemblyLocation(FluentApiFactoryExecutionResult result)
  78. {
  79. if (true == result?.Succeeded)
  80. {
  81. WriteLine($"The generated assembly is {Environment.CurrentDirectory}\\{result.AssemblyFileName}!");
  82. }
  83. else
  84. {
  85. WriteLine("Could not create the assembly!");
  86. }
  87. }
  88. }
  89. }
  90. }

What’s going on

You get the idea? Perfect! Now, before we proceed to see how we can use the assemblies generated in the previous demo, let’s examine what’s going on here.

Using the dynamic assemblies

  1. using System;
  2. using DynamicFluentApis.Core;
  3. using HumanResources.DynamicFluentApis;
  4. using HumanResources.Web.Mvc.DynamicFluentApis;
  5. using static System.Console;
  6. namespace Demo3.ShowcasingFluentApiSupport
  7. {
  8. public class Program
  9. {
  10. public static void Main()
  11. {
  12. // fluent API support demo coming soon...
  13. }
  14. }
  15. }