C # Fluent API with Dynamic Func <> Design

I am fooling myself into creating a small SQL library with a full 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 developed a lot of free APIs before, but everything was much simpler and did not rely heavily on generics. My question is how to implement the implementation of the Build () function. I'm not sure if this is possible (not like this, but maybe using Expression is the key?), But how can I keep track of the common types specified in calls to higher chain methods (e.g. GetOne <> (), WithMany <> ()), so when .Build () is called, Func <> is required, has the correct types?

, Func < > Func < Person, IEnumerable <Pet> , IEnumerable <Address → > () , , - , → .

, ? , , , :

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

... , , , .

.

+4
2

- .Build(person => {}), (person, pet) => {}, .

:

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

- . , , . ,

0

, , , , 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, .

, .

0

Source: https://habr.com/ru/post/1663179/


All Articles