Skip to content
This repository was archived by the owner on Sep 3, 2022. It is now read-only.

Conversation

@mikkelfish
Copy link

Hello, I've created a fluent API for CqlSharp for a few reasons.

  1. I really like the prepare functionality and object binding, but found the actual commands to be "magic strings" that were too complicated for error free behavior.

  2. I wanted to marry the attribute tags defined on a class (column name, partition key, etc) with a command, so that there was as seamless binding as possible; similar to mongo db drivers, etc.

  3. Cql has some quirks that I felt could be made clearer through a fluent syntax. For example, the primary key is the partition key(s) + clustering key(s) but the syntax for compound partition keys is not particularly obvious.

I'm not totally sure that this is ready to merge but thought it was good enough for you to take a look and give comments.

Also I couldn't get the test project to load (don't have a good enough Visual Studio) so made a separate test library using xunit.

Let me know if you have any thoughts! Thanks.

An example query that I've tested is:

  var createTable = Define.CreateTable("test")
                                        .HasPartitionKey("feature_id", typeof(Guid))
                                        .FinishedClusteringKeys
                                        .AddColumn("name", typeof(string))
                                        .AddColumn("indexes_run", typeof(HashSet<string>))
                                        .FinishedDefiningColumns
                                        .WithComment("This is a test")
                                        .SetThrowOnError(false);
   command = new CqlCommand(connection, createTable.BuildString);
   command.ExecuteNonQuery();

   var insert = Manipulate.Insert("test")
                        .With("feature_id")
                        .With("name")
                        .Finished;
   command = new CqlCommand(connection, insert.BuildString, CqlConsistency.One);
   command.Prepare();

   var guid = Guid.NewGuid();
   command.Parameters[0].Value = guid;
   command.Parameters[1].Value = "What2";
   command.ExecuteNonQuery();

    var update = Manipulate.Update("test")
                        .AddSetItem("indexes_run")
                        .FinishedSet
                        .AddWhere("feature_id")
                        .FinishedWhere;

   command = new CqlCommand(connection, update.BuildString, CqlConsistency.One);
   command.Prepare();
   command.Parameters["indexes_run"].Value = new HashSet<string>{"test item"};
   command.Parameters["feature_id"].Value = guid;
   command.ExecuteNonQuery();

Alternatively you could have the following class definition and then generate the fluent automatically with

   [CqlTable("test")]
   class TestClass
   {
            [CqlColumn("feature_id", PartitionKeyIndex=1)]
            public Guid id { get; set; }

            [CqlColumn("name")]
            public string Name { get; set; }

            [CqlColumn("indexes_run")]
            public HashSet<string> IndexesRun { get; set; }
   }


   var create = Define.CreateTable<TestClass>();

  var insert= Modify.Insert<TestClass>();

  var update = Modify.Update(Utilities.Table<TestClass>)
                .AddMapSet("indexes_run")
                .FinishedSet
                .WithStandardWhere<TestClass>(); //Makes sure that all primary keys are included in the where

using the existing prepare with object code.

@reuzel
Copy link
Owner

reuzel commented Jan 4, 2014

Hi!

I saw that you were busy adding a fluent dialect to CqlSharp. First of all, thanks for the interest en effort!

Let me share a few thoughts:

First, I'm not sure yet how to deal with these kind of pull requests. There are basically two options:

  1. merge the code into CqlSharp code base and include it in the next release
  2. move your request into a separate repository, and relate it to CqlSharp "core" through NuGet package references.

To be honest, I tend to go for the second option as it splits the core ADO.NET driver from higher level constructs. For example, what if someone adds an Rx layer? Or what about my own Linq efforts? Merging all these into the core repository seems not wise to me. Splitting it makes that each package will get its own release life-cycle. Furthermore it allows for more control on who contributes on what parts. For example we can set up a new repository CqlSharp.Fluent where we are both authors. I'll make the necessary adjustments in the core layer to accommodate the features you need (e.g. make ObjectAccessor public, and add the required properties).

Second, I wasn't aware that Fakes are a VS Ultimate and Premium only feature. I think this is what prevents you from loading/running the test project. Replacing the Fakes with something else is regrettably not that easy, as I'm faking Time as well as the underlying connections. Changing this is substantial effort (probably requiring the introduction of some factory/dependency injection pattern to create the connection instances). I'll have a look into it. Note that I'm not in favor of using multiple unit-test frameworks in the same solution/repository...

Third, and finally more on content level is that I do not completely understand your thoughts on the API you provide. Why do you need methods like: .FinishedClusteringKeys or .Finished? Is this how it is done with MongoDB? I would expect a Fluent API like:

   Define.CreateTable("blog_comment")
        .WithComment("This is a test");
        .AddPartitionKey("blog_id", CqlType.Uuid)
        .AddClusteringKey("comment_id", CqlType.Timeuuid, Order.Descending)
        .AddColumn("name", CqlType.Varchar)
        .AddColumn("comment", CqlType.Varchar)
        .AddIndexForColumn("name")
        .Execute();

Note the changes:

  1. I'm not using the Finished* methods.
    2, I'm using the CqlTypes to allow for fine-grained control (how else to differentiate between string or Guid types?)
  2. It allows the definition to be executed from its definition.

Again, I'm not sure why you would need the Finished* methods. Is there something I'm missing?

All-in-all a very good start, and thanks for putting your effort into this!

@mikkelfish
Copy link
Author

Thanks for the comments.

The primary reason I put it as a core part was because of accessibility of ObjectAccessor and properties. If that's changed then it makes sense to keep it separate (and gets around the unit test issue).

The point of the Finished* is to provide logical segmentation so that there are not overwhelming choices. For instance, in the create table, there would be probably 30 different actions that could be taken if they were all implemented in one class, so it has been segmented to have so adding partitions, clustering and columns are separate. The Finished part is the way to say that segment is complete. I have seen a few Fluent APIs do a similar thing so that's where I got the idea. Thoughts?

It looks like UUID and string are the only types that have options on storage? Perhaps I will add a special AddColumnAscii and AddColumnTimeGuid then because I'd like to keep it in CLR types as much as possible. I haven't looked at the serialization/deserialization too much; does CqlSharp automatically handle Ascii and TimeGuid conversions?

How would the execution work? Right now the fluent is only to build up a prepared statement so the arguments still need to be set and attached to a command.

@mikkelfish
Copy link
Author

Oh, instead of .Finished

it could be something like

.AddPartitionKeys(
Key.Add(...),
Key.Add(...)
)
.AddColumns(
Column.Add(...),
Column.Add(...)
)

or such

@reuzel
Copy link
Owner

reuzel commented Jan 5, 2014

Hi,

The second option you mentioned seems a lot more logical (and Fluent) to me.

I will create a new repository today or tomorrow, and add you as contributor/manager or whatever role Github provides... Will let you know when it's there. In addition, I'll incorporate the required changes in CqlSharp to enable the Fluent library.

In the mean time I'll have to figure out how to work with unpublished/unstable/development NuGet packages... Perhaps you have an idea? Or must I publish the changes first before the Fluent development can continue?

@mikkelfish
Copy link
Author

Hey, I am ready to work on this again. did you figure out what to do re: the changes?

@reuzel
Copy link
Owner

reuzel commented Jan 18, 2014

I've created a new CqlSharp.Fluent repository and made you one of the members. Currently working on the changes. Hope to finish that in the coming day(s). The challenge is to make it generic for all kinds of extensions, such as your Fluent as well as my own Linq extension. As a result, I'm rewriting the ObjectAccessor to expose all column information in a uniform way. This will make it different than what you propose, but will have all the information you need.

@reuzel
Copy link
Owner

reuzel commented Jan 20, 2014

Just pushed the latest version to NuGet (0.31.0). This should contain the changes you need...

@mikkelfish
Copy link
Author

Sweet, I'll have a look

On Mon, Jan 20, 2014 at 1:54 PM, reuzel notifications@github.com wrote:

Just pushed the latest version to NuGet (0.31.0). This should contain the
changes you need...


Reply to this email directly or view it on GitHubhttps://github.com//pull/23#issuecomment-32728093
.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants