Open GoogleCodeExporter opened 9 years ago
First off, we would want to change the whole deal together like so:
Database.AddTable("Table_Name")
.AddColumn().OfType().WithProperty()
.AddForeignKey().FromColumn("someid").ToPrimaryColumn("primaryid");
something along those lines. you had it close. the t. is repetitive.
For this we could create Helper classes that would allow us to chain everything
together. this way it wouldn't be too drastic of a change. We would create
specific "Options" classes/interfaces:
IColumnOptions, IConstraintOptions, ITableOptions, IForeignKeyOptions etc...
I have little experience with fluent interfaces but this is my understanding of
how
to accomplish a fluent interface with minimal grief. A real good place to look
for
ideas on a fluent interface is ayende's rhino mocks: https://rhino-
tools.svn.sourceforge.net/svnroot/rhino-tools/trunk/rhino-mocks/Rhino.Mocks/
Therefore, ColumnOptions would return a class that can chain together all
options
for columns (WithProperty, OfType etc..) including the options necessary to
continue
the next step in the chain (ITableOptions, IConstraintOptions) something along
those
lines.
Let's create a branch called fluent-interface-refactoring and start playing
with
something there. I would definately like to see this feature implemented.
Original comment by dko...@gmail.com
on 13 Jun 2008 at 3:38
Original comment by dko...@gmail.com
on 13 Jun 2008 at 3:38
heres another link by martin fowler for more info:
http://www.martinfowler.com/bliki/FluentInterface.html
Original comment by dko...@gmail.com
on 13 Jun 2008 at 3:40
This from hammet is something word to take a look at .
Taken from his blog at:
http://hammett.castleproject.org/?p=290 a patch for the migrations tool that
castle
was maintaining.
SchemaBuilder builder = new SchemaBuilder(Schema);
TableBuilder company = builder.AddTable("Company",
Columns.PrimaryKey<int>("Id").Identity(),
Columns.Simple<string>("Name", 25)
);
builder.AddTable("Project",
Columns.PrimaryKey<int>("Id").Identity(),
Columns.Simple<string>("Name", 25),
Columns.ForeignKey("CompanyID", company)
);
builder.AddTable("User",
Columns.PrimaryKey<int>("Id").Identity(),
Columns.Simple<string>("`Key`", 15).MakeUnique(),
Columns.Simple<string>("Name", 25),
Columns.ForeignKey("CompanyID", company)
);
Original comment by gustavo.ringel@gmail.com
on 13 Jun 2008 at 4:00
I like that.
The only thing is, I've always thought that passing an array of columns in the
AddTable method to be clunky. I like it more from your first comment. I think
that
the addtable and addcolumn should be 2 seperate methods.
what do you think?
Original comment by dko...@gmail.com
on 13 Jun 2008 at 11:41
dkode8,
Well, I think I just had a bit of an epiphany - so yes, you could be right.
I was thinking of having to return the Domain Objects from those methods. I
don't
think that would be possible. (AddColumn would return a Column, so how would
you ever
get back to the Table to add another column?) I think we need a Mediator/Builder
returned from each of those methods. It would need to maintain almost a state
machine
to know last table, last column, etc so that if you did AddColumn AddColumn it
would
know that you were adding to the column stack so that OfType would then go to
the
second Column.
I'm setting this fore Milestone-9 - that would be a good release for this?
Original comment by geoffl...@gmail.com
on 13 Jun 2008 at 1:48
that sounds good. I will create a branch for this tonight or tomorrow and start
to
play with some functionality.
Original comment by dko...@gmail.com
on 13 Jun 2008 at 11:53
upon further inspection I think you were correct Geoff.
This is going to require a major refactoring of the TransformationProvider base
class. The current impl, executes each migration right away in isolation.
We would need to refactor the providers to construct the query over time and
end it
with a .Go(); or Execute();
Ideas?
Original comment by dko...@gmail.com
on 15 Jun 2008 at 12:53
I don't think you should start with Database.XXX
Start with SchemaBuilder like Gustavo said. Createa whole different class
completely independent of Database.
SchemaBuild can be sort of a stack-based approach where you keep a reference to
the last table object, last
column object, etc internally. Every method call return the SchemaBuilder
itself.
And then in the end you can do Database.Apply(schemaBuild) or something. That
method will translate the
SchemaBuilder into straight TransformationProvider calls.
Does that make sense? I haven't tried any of this, just in my head. I can
always give this a go as well.
Original comment by geoffl...@gmail.com
on 15 Jun 2008 at 3:40
i've written a couple of tests and created the SchemaBuilder in the fluent
interface
branch.
One thing that flows kind of nice is restricting chainings available by use of
an
interface. If we decide on what steps can be taken when, then we can restrict
the
flow to other steps. This makes the fluent interface a little easier to work
with.
Anyways, the start of it is on the branch take a gander and write a couple of
tests
if anyone wants to. I will have more time throughout the week to work on it.
Original comment by dko...@gmail.com
on 18 Jun 2008 at 2:49
I had an opportunity tonight to make a couple of additional changes to the
branch.
After diving deeper into the foreignkey functionality and foreignkey
constraints I
found that I needed to create a "FluentColumn" decorator to warp Column due to
the
fact that constraints are executed on the fly when invoked via
TransformationProvider. This way we can store the action to perform until the
SchemaBuilder is passed to transformation provider. I will commit the most
recent
changes briefly.
The fluent class SchemaBuilder is pretty much there. minor refactorings needed.
The
last piece of the puzzle is just creating a means for TransformationProvider to
consume the SchemaBuilder instances and translate them into the original
commands.
It feels like there could a pattern to aid in the translation from
SchemaBuilder to
TransformationProvider. Perhaps meditator pattern or something similar.
Original comment by dko...@gmail.com
on 30 Jun 2008 at 2:41
Hey all,
I have been looking through migrator.net sources out of curiousity.
I have a suggestion. might be far off, but i think it could work, without adding
domain objects such as Table, Column, etc:
Each method in the provider, will instantiate a continuation object with 'this'
(the provider) and return it.
Create a set of such interfaces that expose only part of the provider interface, for
each "state" that was discussed. For example, after addColumn there is no logic
in adding table, so the interface that addColumn might return shouldn't expose
those methods of the provider.
ColumnContinuation : IColumnContinuation
- instance of the provider
- methods that exposed from the provider after creating a column.
Original comment by dot...@gmail.com
on 12 Jul 2008 at 11:02
this is already in the works in the method that you describe. To see the
progress
please take a look at the fluent interface branch here:
https://migratordotnet.googlecode.com/svn/branches/fluent.interface
if you would like to submit any patches for adding functionality to this it
would be
apprecated. I haven't had a lot of time to work on it in the last two weeks.
Thanks!
Sean
Original comment by dko...@gmail.com
on 14 Jul 2008 at 12:17
I've looked at the code, however I didn't realize what each interface should
have.
Is there any spec. as to what operations are allowed after each fluent action?
Original comment by dip...@gmail.com
on 16 Jul 2008 at 11:27
At the moment there are no specs as to what each interface should be allowed to
do.
What I was doing was looking at the existing implementation and go strictly off
of
that. ie. Database.AddTable("", new Column(), new Column);
Database.AddForeignKey
etc...
At the moment, the RenameTable/DeleteTable interfaces have to be created. I
didn't
get to that part yet. As well as the ability to pass the SchemaBuilder instance
to
the TransformationProvider at hand to execute the pre-built fluent interface
actions. In some form of:
Database.ExecuteSchemaBuilder(schemaBuilder);
Other than that, I got it half way there for now.
Original comment by dko...@gmail.com
on 16 Jul 2008 at 11:54
Hi dkode8,
I have applied the changes and made the structure that would enable
creating such a schema expression in the provider.
The expressions and creating expressions are implemented using Visitor,
and I may have changed field naming for clarity.
I have not written tests for it, I'd like to know what you guys think.
Thanks.
Original comment by dip...@gmail.com
on 17 Jul 2008 at 10:36
Attachments:
dipidi,
thank you for taking the time to do this.
I have committed your patch along with some additional modifications in
revision 101.
I didn't modify any of your changes, just cleaned some stuff up and moved the
classes into the Migrator.Framework assembly. The ISchemaBuilderExpression
visitor
works perfectly for applying all of the db actions, as well as avoid the need
to
modify TransformationProvider.
The tests are still failing, but only because the Columns collection was
removed
from SchemaBuilder as it is no longer needed as you stated in the code.
I am going to test the fluent interface during the week on a recent project, if
all
is well after a couple of days I think we should merge the changes back into
the
trunk if there are no objections.
on a side note, could you use tabs instead of spaces in your patches in the
future?
no biggie, just trying to keep the formatting straight.
thanks again for the patch!
Sean
Original comment by dko...@gmail.com
on 20 Jul 2008 at 3:44
oh, one other minor thing i forgot to mention that gave me trouble. when you
create
the patch next time, could you base it out of the Migrator root where
Migrator.sln
is? It was looking for migrator/branches/fluent.interface on my local machine
to
apply the patch. this can be avoiding if you generate the patch at the project
root.
thanks!
Sean
Original comment by dko...@gmail.com
on 20 Jul 2008 at 3:47
ok, started testing this out, it seems as there is a problem with the
AddColumnExpression. I am going to verify against SqlExpress, as I have only
tested
with MySql at the moment.
Does anyone know how to turn on logging for migrator? i forget how to do this
Original comment by dko...@gmail.com
on 23 Jul 2008 at 2:12
Hi Sean,
I've finally had some time to run some tests against SqlServer.
It seems the problem was when adding a table which generated an
AddTableExpression,
but it would allow the provider to call AddTable without any columns - which
generates illegal SQL statement (on most DBMS its illegal).
I have attached a fix to this. Generally it deals with the case when the
user adds a table, the next column she will add will be inserted into the
AddTableExpression, such that a new table query is always executed with
the first column added. The rest of the cols will be added independently with
an AddColumnExpression of their own.
On a side note, public virtual void AddTable(string name, params Column[]
columns)
will allow a table to be added with an empty Column[] array.. which will
generate
the same bad SQL.
To make the patch I have used Tabs, and based the patch by the solution root.
Please
let me know if it wasn't made properly.
Have a nice weekend :)
Original comment by dip...@gmail.com
on 25 Jul 2008 at 10:12
Attachments:
cool deal.
i had a hdd failure yesterday so I am re-installing vs2005 now. I will apply
this
patch later this afternoon. thanks!
Sean
Original comment by dko...@gmail.com
on 27 Jul 2008 at 1:59
What is the status of merging the fluent interface branch into the trunk?
Original comment by troygoode
on 12 Aug 2008 at 6:13
hi guys has anyone thought about taking advantage of some syntactic sugar of
object
and collection initializers? I think they read very well but might be a bit
long
winded for some and also having to move to .NET 3.5 and VS2008 might be a bit
of a
sore point for some, just a thought! :)
eg:-
Table table = new Table()
{
Name = "Customer",
Columns =
{
new Column()
{
Name = "Id",
Type = DbType.Int32,
ColumnProperty = ColumnProperty.PrimaryKey
}
}
}
Original comment by flux...@gmail.com
on 27 Aug 2008 at 9:56
[deleted comment]
Oh, here is a sample from the tests (this works as is):
_schemaBuilder
.AddTable("PrimaryKeyTable")
.AddColumn("PrimaryKeyColumn").OfType(DbType.Int32).AsPrimaryKey
.AddTable("SomeTable")
.AddColumn("SomeColumn").OfType(DbType.Int32)
.AddColumn("MyColumnThatIsForeignKey").OfType(DbType.Int32).AsForeignKey
.ReferencedTo("PrimaryKeyTable", "PrimaryKeyColumn")
.WithConstraint(ForeignKeyConstraint.NoAction);
Original comment by dot...@gmail.com
on 8 Sep 2008 at 6:01
Hey all,
I had the time to properly run the tests, for now under sqlserver 2005.
Several Issues:
1.
through a provider when a column is pulled and i want to look at its
properties, not all of them were set -- specifically default value wasn't.
i've wrote down the fetch but there is a need to parse each RDBMS' way of
expressing default values, like ((13)) for 13, and N'abc' for a string 'abc'.
pulling column size isn't specified there, and i left it that way for now.
2.
some existing misconsiderations with the existing fluent interface, fixed a bug
and converted AsForeignKey() to just a property - AsForeignKey. Added
AsPrimaryKey too.
3.
Created a separate test for the fluid interface. the test is dirty in the sense
that it actually uses a concrete provider and does insertions to the database
(takes
them back though) through a transaction.
any feedback would be appreciated, also.. anyone has an idea how to parse the
default
values for each column? from what i know, every RDBMS might have its own format.
Dotan
Original comment by dot...@gmail.com
on 8 Sep 2008 at 6:08
Attachments:
I'm not quite sure how far along with this you guys are, but personally I think
a
fluent interface should read more like:
_schemaBuilder
.AddTable(t => t.Name("PrimaryKeyTable")
.AddColumn(c => c.Name("PrimaryKeyColumn")
.OfType(DbType.Int32)
.AsPrimaryKey)
)
.AddTable(t => t.Name("SomeTable")
.AddColumn(c => c.Name("SomeColumn")
.OfType(DbType.Int32)
)
.AddColumn(c => c.Name("MyColumnThatIsForeignKey")
.OfType(DbType.Int32)
.AsForeignKey(fk => fk.ReferencedTo
("PrimaryKeyTable", "PrimaryKeyColumn")
.WithConstraint
(ForeignKeyConstraint.NoAction)
)
)
);
So we are using lambda function that are applied to builders to generate the
objects. BTW I've just started using migratordotnet in the last couple of days
and I
think is full of creamy goodness. If I get a chance I'll check out the fluent
interface branch.
Keep up the good work!
Cheers
Gordon
Original comment by mail.gor...@gmail.com
on 4 Jun 2009 at 3:39
I'm working on an interface like rails' one:
//automatically adds id, based on IConvention instance
CreateTable("users" t=> {
t.String("name").WithSize(128);
t.Int32("age").NotNullable();
t.Binary("photo");
t.Int32("address_id").AutoForeignKey("addresses");
})
http://code.google.com/p/migratordotnet-fluent/
Original comment by zerova...@gmail.com
on 5 Sep 2009 at 4:25
Has anybody considered an interface like this?
public class CreateSystemUserTable : Migration {
public class SystemUser {
[PrimaryKey]
public Guid SystemUserId { get; set; }
[Length(50)]
public string FirstName { get; set; }
[Length(50)]
public string LastName { get; set; }
[Length(50)]
public string EmailAddress { get; set; }
}
public override void Up() {
Database.AddTable<SystemUser>();
}
public override void Down() {
Database.RemoveTable<SystemUser>();
}
}
public class AddEncryptedPasswordToSystemUser : Migration {
public class SystemUser {
[Length(250)]
public string EncryptedPassword { get; set; }
}
public override void Up() {
Database.AddColumn<SystemUser>();
}
public override void Down() {
Database.RemoveColumn <SystemUser>();
}
}
public class AddAdminUser : Migration {
public class SystemUser {
public Guid SystemUserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string EncryptedPassword { get; set; }
}
public override void Up() {
IEncryptionEngine engine = new AesEncryptionEngine();
Database.Insert(new SystemUser {
SystemUserId=Guid.NewGuid(),
FirstName="Joe",
LastName = "Smith",
EmailAddress = "admin@system.com",
EncryptedPassword=engine.Encrypt("pass")
});
}
public override void Down() {
Database.Delete<SystemUser>(u=>u.EmailAddress=="admin@system.com");
}
}
--
I mean, fluent interfaces are cool and all that but there is so many magic
strings in
your examples and they just look so very fragile.
With the sample from comment 25 it could look something like this:
public class AddAdminUser : Migration {
public class PrimaryKeyTable {
[PrimaryKey(Identity=true)]
public int PrimaryKeyColumn { get; set; }
}
public class SomeTable {
[PrimaryKey(Identity=true)]
public int SomeColumn { get; set; }
[ForeignKey(typeof(PrimaryKeyTable), Constraint=ForeignKeyConstraint.NoAction)]
public int MyColumnThatIsForeignKey { get; set; }
}
public override void Up() {
Database.AddTable<PrimaryKeyTable>();
Database.AddTable<SomeTable>();
}
public override void Down() {
Database.RemoveTable<SomeTable>();
Database.RemoveTable<PrimaryKeyTable>();
}
}
Which is about the same number of characters as that example would be once put
into
an actual migration (my rough attempt comes out to 592 chars this way vs 524 in
comment 25 after whitespace removal but comment 25 fails to specify the primary
key
in the second table and if the keys are identity or not [could be hilo for
instance];
my guess for adding those specs brings c25 up to 555 while this way could
easily be
brought down to 541 chars without sacrificing any readability).
Original comment by after.fa...@gmail.com
on 9 Sep 2009 at 2:08
Original issue reported on code.google.com by
geoffl...@gmail.com
on 7 Jun 2008 at 2:11