C# Fluent API 动态函数<>构造

C# Fluent API With Dynamic Func<> Construction

我正在创建一个 SQL 流利 API 的小型库,我想做这样的事情:

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

                           return person;
                       });

我之前已经构建了很多流畅的 API,但是所有的都简单得多,并且没有那么依赖泛型。我的问题具体是如何着手实施 Build() 结束函数。我不确定它是否可能(看起来不像,但也许使用 Expression 是关键?)但是我如何跟踪在调用更高链方法(例如 GetOne<>( ), WithMany<>()) 以便在调用 .Build() 时所需的 Func<> 类型正确?

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

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

有什么办法可以做到这一点还是我运气不好?似乎很多地方我都在寻找类似的东西:

Func<In1, TResult>
Func<In1, In2, TResult>
Func<In1, In2, In3, TResult>
...etc, etc

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

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

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

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

public class Builder
{
    List<Type> _types = new List<Type>();
    List<object> _values = new List<Object>();
    public Builder GetOne<T>()
    {
        _types.Add(typeof(T));
        // Do stuff
        _values.Add(someObjectYouRetrieved);
        return this;
    }

    public T Build<T1, T>(Func<T1, T2, T> func) =>
        func(_values[0] as T1);

    public T Build<T1, T2, T>(Func<T1, T2, T> func) =>
        func(_values[0] as T1, _values[1] as T2);

    public T Build<T1, T2, T3, T>(Func<T1, T2, T3, T> 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,你首先就没有类型安全。

虽然这是一个权衡。

如果您想要强大的自动完成功能并防止有人在您期望 (person, pet) => {} 时编写 .Build(person => {}),则您需要在构建器中详细说明。

下面是三个深度的示例:

class Person { public IEnumerable<Pet> Pets { get; set;} } class Pet {} class Address{}

public static class Builder
{
    public static Level1<T> GetOne<T>(this object obj, string blah) {
        return new Level1<T>();
    }
}
public class Level1<T1> {
    public Level2<T1, T2> WithMany<T2>(string blah) { return new Level2<T1, T2>(); }
    public T1 Build(Func<T1, T1> pred) { return pred(default(T1)); }
}
public class Level2<T1, T2>
{
    public Level3<T1, T2, T3> WithMany<T3>(string blah) { return new Level3<T1, T2, T3>(); }
    public T1 Build(Func<T1, IEnumerable<T2>, T1> pred) { return pred(default(T1), default(IEnumerable<T2>)); }
}
public class Level3<T1, T2, T3>
{
    public T1 Build(Func<T1, IEnumerable<T2>, IEnumerable<T3>, T1> pred) { 
        return pred(default(T1), default(IEnumerable<T2>), default(IEnumerable<T3>)); 
    }
}

我们在这里获得强类型化的地方:

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

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

关于您关于限制参数数量的说明 - 是的。不幸的是,我不相信在保持强类型输入的同时可以解决这个问题。