使用动态Func的C#Fluent API<>施工 [英] C# Fluent API With Dynamic Func<> Construction

查看:182
本文介绍了使用动态Func的C#Fluent 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< Person,IEnumerable< Pet> ;, IEnumerable< Address>>以便开发人员可以以任何他们需要的方式构建根项目(人员) - 在这种情况下,使用一个>多个查询的结果填充一对集合。



<有没有办法做到这一点,或我运气不好?看起来好像有很多地方我都看过类似的东西:

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

...显然限制您该函数的参数的最大数目。

任何帮助或指针都将非常感谢。

解决方案

如果您希望强大的自动完成功能,并防止某人在您期望<$ c $时编写 .Build(person => {}) (人,宠物)=> {} ,您需要在构建器中使用详细信息。



以下是三个深度级示例:

  class Person {public IEnumerable< Pet>宠物{get; }} 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> With Many< 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> 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(默认(T1),默认(IEnumerable< T2> ),默认(IEnumerable< T3>));


$ / code>

我们在这里打字的地方很强:

  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 = 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;});

至于你对有限参数的说明 - 这是正确的。不幸的是,我不相信在保持强大的打字的同时避免这种情况。


I'm fooling around with creating a small SQL library with a fluent API and want to do something like this:

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;
                       });

I've built plenty of fluent API's before but all have been much more simple and did not rely on generics so heavily. My question is specifically how to go about implementing the Build() end function. I'm not sure if it's even possible (doesn't seem like it but maybe using Expression is the key?) but how can I keep track of the generic types specified in the calls to the higher chain methods (e.g. GetOne<>(), WithMany<>()) so that when the .Build() is called the Func<> that is required is of the correct types?

In the example above I'd want the Func<> to be Func<Person, IEnumerable<Pet>, IEnumerable<Address>> so that the developer can construct the root item (person) in whatever manner they need to - in this case, populating a couple collections with the results of one->many queries.

Is there any way to do this or am I out of luck? It seems like a lot of places I've looked to for similar things do the whole:

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

...type of thing which obviously limits you to a max number of parameters for the function.

Any help or pointers would be much appreciated.

解决方案

If you want strong autocomplete and preventing someone from writing .Build(person => {}) when you're expecting (person, pet) => {}, you'll need to be verbose in your builder.

Here's an example for three levels deep:

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>)); 
    }
}

Where we get strong typing here:

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;
});

Or

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

As to your note about a restricted amount of parameters - that's correct. I don't believe there's anyway to get around that while keeping strong typing, unfortunately.

这篇关于使用动态Func的C#Fluent API&lt;&gt;施工的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆