# 28 Ağustos 2006 Pazartesi
I use CodeDom to generate ActiveRecord classes in ActiveWriter. Quite fancy, but gets complicated easily. To generate something like this:

        public override bool Equals(object obj)

        {

            if (obj == this) return true;

            if (obj == null || obj.GetType() != this.GetType()) return false;

            MyCompositeKey test = (MyCompositeKey)obj;

            return (_keyA == test.KeyA || (_keyA != null && _keyA.Equals(test.KeyA))) &&

              (_keyB == test.KeyB || (_keyB != null && _keyB.Equals(test.KeyB)));

        }


you code this:

        private CodeTypeMember GetCompositeClassEqualsMethod(string className,  List<CodeMemberField> fields)

        {

            CodeMemberMethod equals = new CodeMemberMethod();

            equals.Attributes = MemberAttributes.Public | MemberAttributes.Override;

            equals.ReturnType = new CodeTypeReference(typeof(Boolean));

            equals.Name = "Equals";

 

            CodeParameterDeclarationExpression param = new CodeParameterDeclarationExpression(typeof(Object), "obj");

            equals.Parameters.Add(param);

 

            equals.Statements.Add(new CodeConditionStatement(

                                      new CodeBinaryOperatorExpression(

                                          new CodeFieldReferenceExpression(null, "obj"),

                                          CodeBinaryOperatorType.ValueEquality, new CodeThisReferenceExpression()

                                          ), new CodeMethodReturnStatement(new CodePrimitiveExpression(true))

                                      )

                );

 

            equals.Statements.Add(new CodeConditionStatement

                                      (

                                      new CodeBinaryOperatorExpression

                                          (

                                          new CodeBinaryOperatorExpression(

                                              new CodeFieldReferenceExpression(null, "obj"),

                                              CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(null)),

                                          CodeBinaryOperatorType.BooleanOr,

                                          new CodeBinaryOperatorExpression(

                                              new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(null, "obj"), "GetType"),

                                              CodeBinaryOperatorType.IdentityInequality,

                                              new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "GetType"))

                                          )

                                      , new CodeMethodReturnStatement(new CodePrimitiveExpression(false))

                                      )

                );

 

            equals.Statements.Add(

                new CodeVariableDeclarationStatement(new CodeTypeReference(className), "test",

                                                    new CodeCastExpression(new CodeTypeReference(className),

                                                                            new CodeFieldReferenceExpression(null, "obj"))));

 

            List<CodeExpression> expressions = new List<CodeExpression>();

            foreach (CodeMemberField field in fields)

            {

                expressions.Add(

                    new CodeBinaryOperatorExpression(

                        //_keyA == test.KeyA

                        new CodeBinaryOperatorExpression(

                            new CodeFieldReferenceExpression(null, field.Name),

                            CodeBinaryOperatorType.ValueEquality,

                            new CodeFieldReferenceExpression(new CodeFieldReferenceExpression(null, "test"), field.Name)),

                        CodeBinaryOperatorType.BooleanOr, // ||

                        new CodeBinaryOperatorExpression(

                            //_keyA != null

                            new CodeBinaryOperatorExpression(

                                new CodeFieldReferenceExpression(null, field.Name),

                                CodeBinaryOperatorType.IdentityInequality,

                                new CodePrimitiveExpression(null)

                                ),

                            CodeBinaryOperatorType.BooleanAnd, // &&

                            // _keyA.Equals( test.KeyA )  

                            new CodeMethodInvokeExpression(

                                new CodeFieldReferenceExpression(null, field.Name), "Equals",

                                new CodeFieldReferenceExpression(

                                    new CodeFieldReferenceExpression(null, "test"), field.Name))

                            )

                        )

                    );

            }

 

            CodeExpression expression = null;

            if (expressions.Count > 2)

                expression =

                    new CodeBinaryOperatorExpression(expressions[0], CodeBinaryOperatorType.BooleanAnd, GetBooleanAnd(expressions, 1));

            else

                expression = new CodeBinaryOperatorExpression(expressions[0], CodeBinaryOperatorType.BooleanAnd, expressions[1]);

 

 

            equals.Statements.Add(new CodeMethodReturnStatement(expression));

 

            return equals;

        }


Strangely, there's no CodeBinaryOperatorType.ValueInequality operator defined. (a==b)==false will do the trick, but how about not having an XOR? I find CodeDom's lack of completeness... disturbing.

posted on 28 Ağustos 2006 Pazartesi 21:46:29 UTC  #   
# 19 Ağustos 2006 Cumartesi

ActiveWriter is a DLinq designer like addin for Visual Studio 2005 to design a domain model and to generate code decorated with ActiveRecord attributes.

 

 

It supports / will support:

  • Modeling
    Classes (Almost done)
    Class properties (Almost done)
    Setting a property as primary keys (Done)
    Setting more than one property as composite key (TBI)
    Many to One (Done), One to One, Many to Many relations (TBI)
    Nested classes (TBI)
    ... and more (I'm targeting to support the whole ActiveRecord model)
  • Model validation
    Current build validates most common things like classes without names, spaces in class names etc.
  • Drag and drop of table(s) from Server Explorer
    Can place tables, populate properties. I'm working on relations right now.
  • Automatic generation of source code of the model on save.
    Not implemented yet, but I know how to do it. Right now, it goes through .tt file.
  • Multiple database types as drag/drop source.
    Working on SQL Server right now. Oracle and others will follow.

I use the current CTP of DSL Tools to build the base. There are no downloadable bits right now, since I have to get a VSIP licence to make it run without the SDK.

I don't know to what extent I can open the source. I'll sure make the source downloadable but since part of the code is generated by DSL Tools and there's this VSIP licence, I may not be able to licence it under BSD or something. I'll look for it.

Anyway, I believe it will be a nice addin to have for people working with ActiveRecord/NHibernate. I'm doing my best to release a preview in one or two months.

Comments and suggestions are welcome.


posted on 19 Ağustos 2006 Cumartesi 11:02:33 UTC  #   

It's a black hole since its so massive that even a tiny piece of information on it's usage cannot escape out. Start writing an addin for Visual Studio and you'll find lots of information on the net. Enter into realms of Server Explorer, and you'll feel the massive darkness.

I'm coding and addin for Visual Studio 2005 using DSL Tools. Apart from the relatively steep learning curve of DSL Tools, it was going fine. By the very nature of the project, I started thinking on adding drag and drop of tables from Server Explorer. There are some DSRef and UIHierarchy usage examples on the net, so I thought it will be a piece of cake, until I find out there's no simple way of getting the underlying object model for data connections in Server Explorer, thanks to Microsoft's ability to mark everything Server Explorer as Private or Internal.

Long story short, after long nights and hours of Reflector and thousands of evasive maneuvers against the missile fired by my wife, here's how you get the DbConnection instance of Server Explorer holding the tables dropped on your design surface:

        private static DataConnection GetDataConnection(DTE dte, string itemName)

        {

            IntPtr ptr = IntPtr.Zero;

            try

            {

                if (((IServiceProvider)dte).QueryService(ref ServerExplorer, ref IUnknown, out ptr) >= 0)

                {

                    object o = Marshal.GetObjectForIUnknown(ptr);

                    if (o != null)

                    {

                        object dataConnectionsNode = GetField(o, "lastBrowseObjectNodeSite");

                        if (dataConnectionsNode != null)

                        {

                            INodeSite nodeSite =

                                (INodeSite)dataConnectionsNode;

 

                            INodeSite[] nodes = nodeSite.FindChildrenByLabel(itemName);

                            if (nodes != null && nodes.Length > 0)

                            {

                                object expNode = GetField(nodes[0], "expNode");

                                if (expNode != null)

                                {

                                    object nestedHierarchy = GetField(expNode, "nestedHierarchy");

                                    if (nestedHierarchy != null)

                                    {

                                        DataConnection connection =

                                            GetProperty(nestedHierarchy, "DataConnection") as DataConnection;

                                        if (connection != null)

                                        {

                                            return connection;

                                        }

                                    }

                                }

                            }

                        }

                    }

                }

            }

            finally

            {

                Marshal.Release(ptr);

            }

 

            return null;

        }

        private static object GetField(object o, string fieldName)

        {

            if (o != null && !String.IsNullOrEmpty(fieldName))

                return o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(o);

 

            return null;

        }

 

        private static object GetProperty(object o, string fieldName)

        {

            if (o != null && !String.IsNullOrEmpty(fieldName))

                return o.GetType().GetProperty(fieldName).GetValue(o, null);

 

            return null;

        }

Once you get DataConnection, try ConnectionSupport.ProviderObject property to get the actual DbConnection. Happily, VS loaded addins cannot load new assemblies into AppDomain but at least allowed reflection.

posted on 19 Ağustos 2006 Cumartesi 09:49:55 UTC  #   

...but Foreign Key support is just useless, especially for SqlConnection. Just look at the table signature:

ColumnName DataType Description

constraint_catalog

String

Catalog the constraint belongs to.

constraint_schema

String

Schema that contains the constraint.

constraint_name

String

Name.

table_catalog

String

Table Name constraint is part of.

table_schema

String

Schema that that contains the table.

table_name

String

Table Name

constraint_type

String

Type of constraint. Only "FOREIGN KEY" is allowed.

is_deferrable

String

Specifies whether the constraint is deferrable. Returns NO.

initially_deferred

String

Specifies whether the constraint is initially deferrable. Returns NO.

Quite disappointing, since there's no sign of columns involved. And there's no other GetSchema() way to get them. OracleConnection, on the other hand has a collection called ForeignKeyColumns, which returns columns included for a given FK relationship in a given table. Once you have the FK relation, you should query both sides to get the full column list, though, but better than nothing.

So, with SqlConnection, I use sp_fkeys in ActiveWriter. I wish I could just use GetSchema(), to keep things simple. Sad.

posted on 19 Ağustos 2006 Cumartesi 08:59:42 UTC  #   
# 18 Ağustos 2006 Cuma

My previous blogging attempt was a failure marely because of lack of time and attention. This time, I'm heading the opposite.

Anyway. I'm a consultant living in Istanbul, I code in c# and tell people how to code.

Just a reminder: I restarted blogging today but I posted older projects with historically correct dates. Written in the past, posted today! Don't be confused.

posted on 18 Ağustos 2006 Cuma 22:07:45 UTC  #