DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Data Engineering
  3. Databases
  4. Fluentator - generate fluent API for your structures

Fluentator - generate fluent API for your structures

Pavel Savara user avatar by
Pavel Savara
·
Jan. 04, 12 · Interview
Like (0)
Save
Tweet
Share
7.93K Views

Join the DZone community and get the full member experience.

Join For Free
When working with nested structures like configuration or XML it is a bit of a pain with the syntax in C#. Consider this code below. The object initializes in C# 3.0, but it's still pretty far from ideal. The important point here is readability, which is achieved through nesting the initializers.






var model = new Model();
var pavel = new Employee("Pavel");
model.Companies.Add(new Company("BoldBrick & co.")
{
    Departments = new List<Department>
    {
        new Department("Software & Visions", "swv")
        {
            Teams = new List<Team>
            {
                new Team("Visions")
                {
                    Employees = new List<Employee>
                    {
                        // I was forced to move 
                        // pavel variable declaration completely out of scope
                        pavel,
                        new Employee("Ondra"),
                    },
                    IsAwesome = true,
                },
                new Team("Developers")
                {
                    Employees = new List<Employee>
                    (
                        // I can't do any statements or declarations here
                        // to prepare my data in-place
                        devNames.Select(n=>new Employee(n))
                    )
                    {
                        // note I can't add pavel first
                        pavel,
                    }
                }
            }
        }
    }
});
But there are downsides with approach above. You can't easily add the same instance into 2 nodes. It forces you to declare pavel variable completely out of scope. And you can't use statements to prepare your data in place either. Note how Pavel is inserted after other employees into Developers team. The LINQ Select() helped out a great deal here, but it's not always possible to use it. With more complex models and bigger trees to build, this will become an unmanageable mess.

So, extension methods and lambdas come to rescue. Do you like the code below better? I certainly do. I can use statements and variable declarations within inner scope. I get more dense and readable code.

var model = new Model();
model.AddCompany("BoldBrick & co.", bb =>
{
    bb.AddDepartment("Software & Visions", "swv", swv =>
    {
        // variable is still bit out of scope, but not in the root scope
        var pavel = new Employee("Pavel");
        swv.AddTeam("Visions", visions =>
        {
            visions.AddEmployee(pavel);
            visions.AddEmployee("Ondra");
            visions.IsAwesome = true;
        });
        swv.AddTeam("Developers", devs => 
        {
            devs.AddEmployee(pavel);
            // I can add more employees after Pavel
            devs.AddEmployees(devNames.Select(n => new Employee(n)), dev=>
            {
                dev.Age = 33;
            });
            // and also can use any complex statement in-place
            for (int i = 0; i < devNames.Count; i++)
            {
                int ix=i;
                devs.AddEmployee(devNames[i], dev =>
                {
                    dev.Age = ix;
                });
            }
        });
    });
);});

How does the extension method look?

Below is the extension method over external structure Department, which accepts the same parameters as the Team constructor. Inside is instance creations and adding to the collection. Finally to allow the nesting of scopes, we pass the new instance to Action<> delegate.
static public Team AddTeam(this Department self, 
              string name, Action<Team> result = null)
{
    var item = new Team(name);
    self.Teams.Add(item);
    if (result != null) result(item);
    return item;
}

Generate the extensions

The extension method above is nice and useful but it's quite tedious to write it for each combination of container and child item - multiplied by all the constructor signatures. So I decided to create ReflectionFluentator which can generate the code for you. It reads your model via reflection and generates the C# extensions. See the sample on how to use the generator.

And the same thing for XML? Sure. You provide the XSD to XsdFluentator. See the sample on how to use the generator.

var doc=new XDocument();
doc.AddLibrary("Prague",prague =>
{
    prague.AddBook("Saturnin", book =>
    {
        book.AddAuthor("Zdenek Jirotka");
    });
    prague.AddBook("Bylo Nas 5", book =>
    {
        book.AddAuthor("Karel Polacek");
    });
});
This code can generate this XML as expected.
<library id="Prague"
  xmlns="http://polyglottos.googlecode.com/svn/trunk/demomodel/library.xsd">
  <book name="Saturnin">
    <author name="Zdenek Jirotka" />
  </book>
  <book name="Bylo Nas 5">
    <author name="Karel Polacek" />
  </book>
</library>
Note that both generators are prototypes and not ready to ship. They don't handle any edge scenarios when reading the metadata or writing the code. Fluentator is part of the Polyglottos project. If you like the idea and wish to contribute improvements, please find my profile's contact info.

At this point some of you may wonder what else could be made fluent this way. In my case, I realized that I need to generate the code and the code is just a nested structure. So I created the CodeDom code generator Polyglottos with fluent API. That's for another article.


Source:  http://zamboch.blogspot.com/2011/12/fluentator-generate-fluent-api-for-your.html
API

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 5 Steps for Getting Started in Deep Learning
  • Create a CLI Chatbot With the ChatGPT API and Node.js
  • Use AWS Controllers for Kubernetes To Deploy a Serverless Data Processing Solution With SQS, Lambda, and DynamoDB
  • [DZone Survey] Share Your Expertise and Take our 2023 Web, Mobile, and Low-Code Apps Survey

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: