Paul Gielens:ThoughtsService

another Endpoint to my thoughts

News

Syndication

Ads


Favorites

Projects

June 2004 - Posts

Dutch .NET Developers Alliance welcomes...

And we welcome Jonne Kats! Are you a Dutch .NET developer, willing to communicate with colleagues working in the same field? Drop me a line!

Get the “Dutch .NET Developers Alliance” opml to be imported in your favorite RSS reader here

Posted: Jun 29 2004, 05:52 PM by p.gielens
Filed under:
Download Tech-Ed decks with the TechEdDeckDownloader class

*Update: nothing to see here.

Posted: Jun 28 2004, 11:04 PM by p.gielens | with 14 comment(s)
Filed under:
Dutch .NET Developers Alliance welcomes...

Today we welcome Mike van Zandwijk and Olle de Zwart!

 

Get the “Dutch .NET Developers Alliance” opml to be imported in your favorite RSS reader here.

Posted: Jun 28 2004, 05:13 PM by p.gielens | with 3 comment(s)
Filed under:
Data in Services-Oriented Architecture

Since I’m not feeling to well (shitty fever) I had plenty of opportunity to watch a few Tech-Ed 2004 streams. Hopefully I’ll be able to make it to the DotNed Usergroup meeting next Monday where Juval Lowy and Ingo Rammer will give their talks. I believe Christian Weyer will also be around.

A must see is the ARC402 - Data in Services-Oriented Architecture session. Harry Pierson explains why each model’s strength is simultaneously its weakness related to SOA. A great no nonsense presentation which raises the curtain on the abstractness and hype which seems to be the equivalent of SOA thinking these days.

Posted: Jun 26 2004, 06:44 PM by p.gielens | with 2 comment(s)
Filed under:
Declarative transactions at method level with Neo

I have this business requirement to interact with message queues, binding queue and Neo related operations together in a transaction to ensure consistency. My team is comfortable with using COM+ services in the ADO.NET world so I figured why not add COM+ support to Neo. Recently I picked up on this article http://www.apache.org/~hammett/articles/dotnettransactions.html which describes a nice way to satisfy my external transaction requirements.

 

Listed below is the CustomerOrderBroker class which creates a new transaction. It then fetches the order message from the queue and attempts to process it. Though the code isn’t that useful it actually does express my initial intention. Both the queue and Neo’s operations are rolled back! See to log down below.

 

Listing CustomerOrderBroker class:

 

[TransactionAware]

public class CustomerOrderBroker : ContextBoundObject

{

      private static ILog logger;

 

      public CustomerOrderBroker()

      {

            if (logger == null)

                  logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.ToString());

      }

 

      [RequiresTransaction(TransactionOption.RequiresNew)]

      public bool ProcessQueuedOrder()

      {

            try

            {

                  logger.Debug("ProcessQueuedOrder Tx: " + ContextUtil.TransactionId.ToString());

 

                  // fetch the new order from queue

                  MessageQueue neoQueue = new MessageQueue(@".\private$\neo");

                  Message orderMessage = neoQueue.Receive(new TimeSpan(0, 0, 0, 1), MessageQueueTransactionType.Automatic);

 

                  // assume this is a new customer, insert a new

                  ObjectContext context = NorthwindHelper.GetFreshObjectContext();

                  Customer customer = new CustomerFactory(context).CreateObject("PGIE");

                  customer.CompanyName = "ObjectAutomation";

                  context.SaveChanges();

 

                  // imagine we filled a new order entity with the order message data

                  Order order = new OrderFactory(context).CreateObject(1);

                  OrderStatus status = customer.PlaceOrder(order);

 

                  if (status == OrderStatus.Accepted)

                  {

                        ContextUtil.SetComplete();

                        return true;

                  }

                  else

                  {

                        // this will rollback both Neo's and msmq's work

                        ContextUtil.SetAbort();

                        return false;

                  }

            }

            catch (Exception ex)

            {

                  ContextUtil.SetAbort();

                  throw new Exception("Failed to process queued order.", ex);

            }

            finally

            {

                  logger.Debug("ProcessQueuedOrder voted: " + ContextUtil.MyTransactionVote.ToString());

            }

      }

}

 

And the transaction aware Customer class.

 

[TransactionAware]

public class Customer : CustomerBase

{

      private static ILog logger;

 

      protected Customer(System.Data.DataRow aRow, Neo.Core.ObjectContext aContext) : base(aRow, aContext)

      {

            if (logger == null)

                  logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.ToString());

      }

 

      [RequiresTransaction(TransactionOption.Supported)]

      public OrderStatus PlaceOrder(Order order)

      {

            try

            {

                  logger.Debug("PlaceOrder Tx: " + ContextUtil.TransactionId.ToString());

 

                  // we did our work

                  ContextUtil.SetComplete();

 

                  // insufficient credit for customer, the coordinating process has to decide

                  // what action would be appropriate

                  return OrderStatus.Rejected;

            }

            catch (Exception ex)

            {

                  ContextUtil.SetAbort();

                  throw new Exception("Failed to place order.", ex);

            }

            finally

            {

                  logger.Debug("PlaceOrder voted: " + ContextUtil.MyTransactionVote.ToString());

            }

      }

}

 

 

And the log produced by the Unit-Test:

 

00:09:15,140: Initialised new transaction sink.

00:09:15,156: Initialised new transaction required enviroment.

00:09:22,000: Initialised new, new transaction required enviroment.

00:09:22,015: ProcessQueuedOrder Tx: 33725a5e-d05c-465b-a306-692bc12b87bf

00:09:22,078: Created new SqlDataStore.

00:09:22,093: Initialised new object context.

00:09:22,093: Initialised new transaction sink.

00:09:22,156: Creating object of type Northwind.Model.Customer

00:09:22,312: Initialised new transaction required enviroment.

00:09:22,328: Initialised new supported transaction enviroment.

00:09:22,328: SaveChanges. Beginning.

00:09:22,359: Opening connection to "data source=localhost;initial catalog=northwind;

user id=sa;password=******"

00:09:22,640: INSERT INTO Customers (Region, PostalCode, CustomerId, Fax, ContactName, City, Address, ContactTitle, Phone, CompanyName, Country) VALUES (@Region, @PostalCode, @CustomerId, @Fax, @ContactName, @City, @Address, @ContactTitle, @Phone, @CompanyName, @Country)

00:09:22,640:     @Region =

00:09:22,640:     @PostalCode =

00:09:22,640:     @CustomerId = PGIE

00:09:22,640:     @Fax =

00:09:22,640:     @ContactName =

00:09:22,640:     @City =

00:09:22,640:     @Address =

00:09:22,640:     @ContactTitle =

00:09:22,640:     @Phone =

00:09:22,640:     @CompanyName = ObjectAutomation

00:09:22,640:     @Country =

00:09:22,671: 1 rows affected

00:09:22,687: Closed connection to "data source=localhost;initial catalog=northwind;user id=sa;"

00:09:22,703: SaveChanges. Completed.

00:09:22,703: Creating object of type Northwind.Model.Order

00:09:22,718: PlaceOrder Tx: 33725a5e-d05c-465b-a306-692bc12b87bf

00:09:22,718: PlaceOrder voted: Commit

00:09:22,718: ProcessQueuedOrder voted: Abort

 

 

 

Posted: Jun 20 2004, 12:30 AM by p.gielens | with 3 comment(s)
Filed under: ,
Did Microsoft lost the API war?

Joel’s latest article confused me a bit. Isn’t it that Visual Basic really sucks, and I mean really sucks! Let’s say it sucks terribly bad for doing anything other then drag and drop RAD development. At least for people that know a thing or two about JAVA. C++ is a great tool for the more experienced developer, but since Microsoft messed up with COM, we’ve been left with a confusing, difficult programming model. Not to mention DCOM, COM+ etc. Joel is right about memory management though, but I personally don’t consider it to be a show stopper.

Now we have .NET, it’s a great platform today, I don’t care about 2006, most likely it’ll be a great platform in 2006+. I’ll even consider investing development time to run my applications on mono (who would have thought of that? I’m not geek enough to fit in with the Tux clan). No ROI, heck I’ll interop. If it wasn’t for .NET I would be doing JAVA (I probably would If I stayed away from CORBA).

So besides Microsoft refusing to fix .NET bugs today, overloading developers with 2006 stuff, what’s the big deal?

Kickoff

Germany vs Netherlands, place your bets

2:1, I'm almost ashamed :D

Neo now supports Oracle!

Neo (1.2.1) has support for Oracle. Erik refactored the classes within the Neo.Database namespace to promote the implementation of additional DataStores. Something that according to Erik should be very easy as of now. Oracle support comes along with a series of extensive Unit Tests with a big thanks to Arjen Poutsma who did the original implementation!

The refactorings include the data store base class, DbDataStore implementing the IDataStore interface which is the class of choice to derive additional DataStores from. The cumbersome SQL generation is encapsulated in the GenericSql92Builder class. One could mistaken the GenericSql92Builder class for the Query Object pattern (PoEAA, Fowler 2003), which in fact it is not. It’s responsibility doesn’t reach further then generate SQL-92 compliant syntax for a DataTable and a specific IDbImplementationFactory instance passed in the constructor. SQL is generated by interpreting Qualifiers and FetchSpecifications which results in all the necessary SQL queries to perform CRUD and find operations on persistent entity data.

The following UML diagram illustrates the classes and interfaces involved.

Download the 1.2.1 distribution

Posted: Jun 11 2004, 06:26 PM by p.gielens
Filed under:
Object Context

Applications build on top of the Neo framework can use a shared context (one per execution thread to be exact) or create explicit contexts. Personally I prefer using explicit  context on a use-case basis. The ObjectContext (the class encapsulating context) handles CRUD operations. The context expressed by the ObjectContext class actually relies on its internal DataSet (remember Neo is an Object Façade over ADO.NET). As you’ve seen in my previous post (and specifically the comment Alex made :D), factories create entity objects within a context. They use the specific context passed to the constructor. The ObjectContext class implements the IDataStore interface which tells us that the ObjectContext class uses a data store to retrieve and save changes to entity objects. Examples of data stores are: sql server instance, DataSet, XML, ASCII etc.

Another cool thing of Neo’s contexts is that they can be nested. This is according to Erik a non-obvious feature which is very useful for rich client applications featuring master / detail forms. You then push the masters context into the detail context, call SaveChanges in the detail context and it’ll escalate into the parents context. Canceling a use-case in the detail form just deletes the detail context. Erik explains this a bit better then I do here.

In a future post I’ll discuss the internals of Neo’s ObjectContext architecture, explaining how it’s hooked into ADO.NET fundaments.

Posted: Jun 06 2004, 01:19 PM by p.gielens
Filed under: ,
Finding Domain Objects

I’d like to discuss finding domain objects using Neo against the Northwind catalog. The code has been tested against Neo 1.2.1 and the following schema:

 

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>

<!DOCTYPE database SYSTEM "file:norque.dtd">

<?neo path="../../../Tools/CodeGen/Resources"?>

<database name="northwind" package="Northwind.Model" defaultIdMethod="none">

  <table name="Customers" javaName="Customer">

    <column name="CustomerId"    javaName="Id" primaryKey="true" required="true" type="CHAR" size="5" />   

    <column name="CompanyName"   javaName="CompanyName"          required="true" type="VARCHAR"    size="40" />   

    <column name="ContactName"   javaName="ContactName"                          type="VARCHAR"    size="30" />   

    <column name="ContactTitle"  javaName="ContactTitle"                         type="VARCHAR"    size="30" />     

    <column name="Address"       javaName="Address"                              type="VARCHAR"    size="60" />

    <column name="City"          javaName="City"                                 type="VARCHAR"    size="15" />

    <column name="Region"        javaName="RegionCode"                           type="VARCHAR"    size="15" />

    <column name="PostalCode"    javaName="PostalCode"                           type="VARCHAR"    size="10" />

    <column name="Country"       javaName="Country"                              type="VARCHAR"    size="15" />

    <column name="Phone"         javaName="PhoneNumber"                          type="VARCHAR"    size="24" />

    <column name="Fax"           javaName="FaxNumber"                            type="VARCHAR"    size="24" />

  </table> 

</database>

 

Neo separates finder behavior in it’s entity factories which can be packaged within the domain layer (the layer where you’re model classes reside). You then have three different interfaces to find your domain objects, Templates, Qualifiers, and FetchSpecifications.

 

Templates

 

Templates are particularly useful because they have all the properties as the corresponding entity object, including to-one relationships.

 

[Test]

public void SalesRepresentativeWithTemplate()

{

      CustomerList result;

      CustomerTemplate t;

 

      t = new CustomerFactory(context).GetQueryTemplate();

      t.ContactTitle = "Sales Representative";

 

      result = new CustomerFactory(context).Find(t);

      Assertion.AssertEquals("Found wrong number of sales representative", 17, result.Count);

}

 

If you study the Neo documentation closely you’ll notice the methode FindMatchingObjects being refactored to a simple Find overload. Templates work out really well if being used close to the domain specific code.

 

Qualifiers

 

Qualifiers define search criteria’s.

 

[Test]

public void SalesRepresentativeWithPropertyQualifier()

{

      PropertyQualifier q;

      IList result;

 

      q = new PropertyQualifier("ContactTitle", new EqualsPredicate("Sales Representative"));

      result = new CustomerFactory(context).Find(q);

      Assertion.AssertEquals("Found wrong number of sales representative", 17, result.Count);

}

 

As you might have noticed the find method returns an untyped collection. There’s also an overloaded Find which accepts a qualifier format string and in fact does return a CustomerList collection. Another neat feature of qualifiers is that it can use inlined values and multiple clauses. Qualifiers make perfect sense for relative simple search scenario’s.

 

FetchSpecifications

 

Then there is the FetchSpecification. Using a FetchSpecification for searching domain objects enables you to page and sort results. This is done by passing a value indicating the fetch limit, and sort ordering in the constructor.

 

[Test]

public void SalesRepresentativesWithFetchSpecification()

{

      PropertyQualifier q;

      FetchSpecification fSpec;

      CustomerList result;

 

      q = new PropertyQualifier("ContactTitle", new EqualsPredicate("Sales Representative"));

      fSpec = new FetchSpecification(context.EntityMapFactory.GetMap(typeof (Customer)), q);

      result = new CustomerFactory(context).Find(fSpec);

      Assertion.AssertEquals("Found wrong number of sales representative", 17, result.Count);

}

 

[Test]

public void SalesRepresentativesWithLimit()

{

      PropertyQualifier q;

      FetchSpecification fSpec;

      CustomerList result;

 

      q = new PropertyQualifier("ContactTitle", new EqualsPredicate("Sales Representative"));

      fSpec = new FetchSpecification(context.EntityMapFactory.GetMap(typeof (Customer)), q, 10);

      result = new CustomerFactory(context).Find(fSpec);

      Assertion.AssertEquals("Should only fetch up to limit.", 10, result.Count);

}

 

Overview finders interface implemented in CustomerFactory class:

 

public Northwind.Model.CustomerList Find ( Northwind.Model.CustomerTemplate template )

    Member of Northwind.Model.CustomerFactory

 

public Northwind.Model.CustomerList Find ( Neo.Core.FetchSpecification fetchSpecification )

    Member of Northwind.Model.CustomerFactory

 

public new Northwind.Model.CustomerList Find ( System.String qualifierFormat , object[] parameters )

    Member of Northwind.Model.CustomerFactory

 

public new Northwind.Model.CustomerList FindAllObjects (  )

    Member of Northwind.Model.CustomerFactory

 

public new Northwind.Model.Customer FindFirst ( System.String qualifierFormat , object[] parameters )

    Member of Northwind.Model.CustomerFactory

 

public Northwind.Model.Customer FindObject ( System.String pkvalue )

    Member of Northwind.Model.CustomerFactory

 

public new Northwind.Model.Customer FindUnique ( System.String qualifierFormat , object[] parameters )

    Member of Northwind.Model.CustomerFactory

 

public new Northwind.Model.CustomerList FindWithLimit ( System.Int32 limit , System.String qualifierFormat , object[] parameters )

    Member of Northwind.Model.CustomerFactory

 

Factoring the finder logic into entity factory classes where the Neo.Framework.ObjectFactory class acts as the super type seems like a natural fit. One of the things I really like about Neo is it’s versatility for finding domain objects. Though finder logic can complicate a frameworks API, Neo seems to do a great job of packaging a lot of functionality but at the same time maintaining a simple and intuitive API.

 

The Neo distribution comes with a best practices document (neo-x.x.x\neo\docs) which explains when to use what a bit further.

Posted: Jun 05 2004, 01:56 PM by p.gielens | with 5 comment(s)
Filed under: ,
More Posts Next page »