具有动态function构造的C#Fluent API

我正在为创建一个具有流畅API的小型SQL库而烦恼,并希望做到这样的事情:

var person = connection.GetOne("select * from [Person] where [Id] = 1") .WithMany("select * from [Pet] where [PersonId] = 1") .WithMany
("select * from [Address] where [PersonId] = 1]") .Build((person, pets, addresses) => { person.Pets = pets; person.Addresses = addresses; return person; });

我之前已经构建了大量流畅的API,但所有这些API都变得更加简单,并且不依赖于generics。 我的问题是具体如何实现Build()结束函数。 我不确定它是否可能(看起来不像它但可能使用Expression是关键?)但是如何跟踪调用更高链方法中指定的generics类型(例如GetOne ( ),WithMany ())这样当调用.Build()时,所需的Func 是正确的类型?

在上面的示例中,我希望Func 为Func <Person,IEnumerable ,IEnumerable

>,以便开发人员可以以他们需要的任何方式构造根项(person) – 在这种情况下,用一个多个查询的结果填充一些集合。

有没有办法做到这一点,还是我运气不好? 似乎很多地方我都希望类似的东西可以做到:

 Func Func Func ...etc, etc 

…显然将您限制为函数的最大参数数量的事物类型。

任何帮助或指针将不胜感激。

如果你想要强大的自动完成function并阻止某人在你期待(person, pet) => {}时编写.Build(person => {}) ,你需要在你的构建器中使用详细信息。

以下是三个级别的示例:

 class Person { public IEnumerable Pets { get; set;} } class Pet {} class Address{} public static class Builder { public static Level1 GetOne(this object obj, string blah) { return new Level1(); } } public class Level1 { public Level2 WithMany(string blah) { return new Level2(); } public T1 Build(Func pred) { return pred(default(T1)); } } public class Level2 { public Level3 WithMany(string blah) { return new Level3(); } public T1 Build(Func, T1> pred) { return pred(default(T1), default(IEnumerable)); } } public class Level3 { public T1 Build(Func, IEnumerable, T1> pred) { return pred(default(T1), default(IEnumerable), default(IEnumerable)); } } 

我们在这里powershell打字:

 obj.GetOne("select * from [Person] where [Id] = 1") .WithMany("select * from [Pet] where [PersonId] = 1") .WithMany
("select * from [Address] where [PersonId] = 1]") .Build((person, pets, addresses) => { person.Pets = pets; return person; });

要么

 obj.GetOne("select * from [Person] where [Id] = 1") .WithMany("select * from [Pet] where [PersonId] = 1") .Build((person, pets) => { return person; }); 

关于有限数量参数的说明 – 这是正确的。 不幸的是,我不相信在保持powershell打字的同时解决这个问题。

这取决于您希望能够添加多少个方法,但每个方法都应该使用相同的方法返回另一个类型为T对象。 这要求您为构建函数中的每个类型参数重复所有函数。

也许不是你的问题的答案,但你可以放弃静态类型检查并使用动态的东西:

 public class Builder { List _types = new List(); List _values = new List(); public Builder GetOne() { _types.Add(typeof(T)); // Do stuff _values.Add(someObjectYouRetrieved); return this; } public T Build(Func func) => func(_values[0] as T1); public T Build(Func func) => func(_values[0] as T1, _values[1] as T2); public T Build(Func func) => func(_values[0] as T1, _values[1] as T2, _values[2] as T3); // Add more for the amount of type params you want to allow } 

这并没有给你无限的灵活性,但至少它可以防止方法爆炸。

它也没有给你静态类型检查,但是因为你正在做SQL,所以你首先没有类型安全。

这是一个权衡。