Language And Object Relational Mapping Demo

This demo, based on SqlServer, is meant to showcase the strongly typed excellence and range of the WYSE framework, along with its groundbreaking (Hierarchical) Object Relation Mapping capabilities. The idea is to be able to code without having to make code quality damaging compromises.

...and since code speaks louder than words demo code (stack trace) and database results are shown in a developer-tools-like panel as you interact with the server.

Below you can find, grouped firstly by SQL statement type and later by object relational mapping feature, a list of links. These links are server calls in which increasingly advanced WYSE features of language and ORM are used.

Plain Text Expression Building

Be sure to check out the Report Generator demo. That playable demo explores what is possible in combination with self-constructed plain text where clauses. So it is functionally similar to GraphQL.

Working With State (BETA)

The 'Generic Mappings With SelectBuilder' examples listed above showcase how objects can be fully instantiated hierarchically. But that is only the 'reading' part. The 'writing' part is where State comes into play. With this, given an object with properties that have properties and so forth, a script will be generated to sync the state of the database with with that of the object. This script will contain all the necessary inserts, updates and/or deletes. The deletes are important when a property is a list of objects, so with 1:n relationships.

The small window you are using greatly diminishes your demo experience. Please consider viewing using a larger window.

Behind The Scenes Explanation

This is where you get to see what's happening behind the scenes when you interact with the site on the left side.

The view consists of three (collapsible) parts...

1. Full Code, Queries, And Results

The server handles the web requests that are the result of you interacting with the site.
In doing so, the demo backend code is executed which in turn uses WYSE.

The relevant/notable methods taking part in that process are shown in the first vertical tab.
Basically you are seeing a (partial) stack trace.
It is tree structure of which the nodes are collapsible/expandable for your browsing convenience.

The leaves of the STACK TRACE TREE are where interaction with the database takes place.
That is generally where you will find the generated SQL statements and the JSON of the converted database results.

2. Queries And Results Only

A filtered list of entries where only the database queries and results are shown.

3. API Results

If applicable: this shows the JSON of the call to the server as if it had been an API call.

  • Trace Entries: 2
      ...LanguageAndORM.Controllers.HomeController.HierarchicalStatePersistence:
      [HttpGet]
      public ViewResult HierarchicalStatePersistence()
      {
          var traceTree = _traceTreesRepository.Record();
          traceTree.RecordCodeMethodExecution(MethodBase.GetCurrentMethod());
      
          _hormsService.GetHierarchicalStatePersistenceResult(traceTree);
      
          return CreateView(_traceTreesRepository.RootTraceTree);
      }
    • Trace Entries: 3
        ...LanguageAndORM.Business.HormsService.GetHierarchicalStatePersistenceResult:
        public void GetHierarchicalStatePersistenceResult(TraceTree parentTraceTree)
        {
            var traceTree = parentTraceTree.AddChild();
            traceTree.RecordCodeMethodExecution(MethodBase.GetCurrentMethod());
        
            const int newCustomerId = 3;
            // A new customer with a home address and purchases with a delivery to home address.
            var customerState = CreateNewCustomerStateHierarchy(newCustomerId);
        
            var database = new Database();  // Collection of demo tables
            var statePersistenceScriptBuilder = new /*WYSE*/ScriptBuilder(
                new /*WYSE*/ListResolver(database.Tables),
                new /*WYSE*/ExecutableFactory(
                    // This demo wraps inserts with SqlServer's 'SET IDENTITY_INSERT ON/OFF'
                    // statements.
                    // For more identity control per table supply your own interface implementation.
                    new /*WYSE*/ExecutableFactory.Settings(
                        includeIdentityFieldWithInsertsPredicate: hasIdentityColumn)),
                // Post processing: In this demo it is only identity_insert wrapping of the
                // insert statements.
                // Again, this is open, so you can supply your own interface implementation.
                new /*WYSE*/IdentityInsertionWrapper());
        
            var statePersistenceScript = statePersistenceScriptBuilder
                .BuildStatePersistenceScript(customerState);
        
            var genericMappings = GenericMappingsRepository.AllTableBased
                /*WYSE*/.DownstreamOf<CustomerTable>();
        
            var selectResult = createResultSelect(database.CustomerTable);
        
            using SqlConnection connection = _connectionFactory.Create();
            connection.Open();
            using var transaction = connection.BeginTransaction();
        
            executeScriptAndRecordResult(statePersistenceScript, selectResult);
        
            // Mark state as persisted and reset cached persisted state with current object state,
            // recursively.
            // This tells WYSE that the object state and database state are equivalent/synced.
            customerState.ResetPersistedValue(recursive: true);
        
            // Update to customer, purchase 1 marked for deletion, Purchase 2 updated,
            // new delivery address for purchase 2.
            ChangeCustomerStateHierarchy(customerState);
        
            statePersistenceScript = statePersistenceScriptBuilder
                .BuildStatePersistenceScript(customerState);
        
            executeScriptAndRecordResult(statePersistenceScript, selectResult);
        
            transaction.Rollback();
            connection.Close();
        
            static bool hasIdentityColumn(/*WYSE*/IMutatableList list)
                => list is /*WYSE*/ISqlServerTable table && table.HasAutoIncrementColumn();
        
            /*WYSE*/Select createResultSelect(CustomerTable customerTable)
            {
                var selectBuilder = new /*WYSE*/SelectBuilder<CustomerTable, Customer>(
                    database.CustomerTable,
                    genericMappings)
                {
                    Where = new /*WYSE*/Where(database.CustomerTable.Id == newCustomerId)
                };
                var select = selectBuilder.Build();
        
                _identifiersResetter.ResetAliases(select);
        
                return select;
            }
        
            void executeScriptAndRecordResult(Script statePersistenceScript, Select selectResult)
                => _sqlExecuter.ExecuteScript(
                    transaction,
                    statePersistenceScript + selectResult,  /* Create combined script */
                    reader => reader./*WYSE*/GetItemTree(
                        selectResult,
                        database.CustomerTable.Convert,
                        genericMappings),
                    traceTree);
        }
      • Trace Entries: 2
          ...LanguageAndORM.DatabaseAccess.SqlExecuter.ExecuteScript:
          public TItem ExecuteScript<TItem>(
              SqlTransaction transaction,
              /*WYSE*/IScript script,
              Converter<DbDataReader, TItem> converter,
              TraceTree parentTraceTree)
          {
              var traceTree = parentTraceTree.AddChild();
              traceTree.RecordCodeMethodExecution(MethodBase.GetCurrentMethod());
          
              using SqlCommand command = transaction.Connection.CreateCommand()
                  // A WYSE Render context determines certain (overridable) render settings.
                  // Here the context is SqlServer. More SQL flavors are supported.
                  // A WYSE render builder is used for stringifying
                  // the strongly typed SQL statements.
                  /*WYSE*/.For(script, _renderContextFactory.Create(), _createRenderBuilder);
              command.Transaction = transaction;
          
              try
              {
                  using DbDataReader reader = command.ExecuteReader();
          
                  var item = converter(reader);
          
                  traceTree.RecordDbCommandExecutionSuccess(
                      command.CommandText,
                      command.Parameters.ToDictionary(),
                      item);
          
                  return item;
              }
              catch (Exception exception)
              {
                  traceTree.RecordDbCommandExecutionFailure(
                      command.CommandText,
                      command.Parameters.ToDictionary(),
                      exception);
          
                  return default;
              }
          }

          Command Text:

          SET IDENTITY_INSERT [Address] ON;
          
          INSERT INTO [dbo].[Address]
          ([Id], [Town], [Confirmed], [LastChanged], [ExternalId])
          VALUES (21, 'HomeAddress', 1, NULL, NULL);
          
          SET IDENTITY_INSERT [Address] OFF;
          
          SET IDENTITY_INSERT [Delivery] ON;
          
          INSERT INTO [dbo].[Delivery]
          ([Id], [Date], [AddressId])
          VALUES (4, '2025-05-23 00:00:00.000', 21);
          
          SET IDENTITY_INSERT [Delivery] OFF;
          
          SET IDENTITY_INSERT [Delivery] ON;
          
          INSERT INTO [dbo].[Delivery]
          ([Id], [Date], [AddressId])
          VALUES (5, '2025-06-06 00:00:00.000', 21);
          
          SET IDENTITY_INSERT [Delivery] OFF;
          
          SET IDENTITY_INSERT [Customer] ON;
          
          INSERT INTO [dbo].[Customer]
          ([Id], [FullName], [HomeAddressId], [BillingAddressId])
          VALUES (3, 'John Smith', 21, NULL);
          
          SET IDENTITY_INSERT [Customer] OFF;
          
          SET IDENTITY_INSERT [Purchase] ON;
          
          INSERT INTO [dbo].[Purchase]
          ([Id], [CustomerId], [ProductId], [DeliveryId], [PaymentMethodId])
          VALUES (5, 3, 1, 4, 1);
          
          SET IDENTITY_INSERT [Purchase] OFF;
          
          SET IDENTITY_INSERT [Purchase] ON;
          
          INSERT INTO [dbo].[Purchase]
          ([Id], [CustomerId], [ProductId], [DeliveryId], [PaymentMethodId])
          VALUES (6, 3, 2, 5, 3);
          
          SET IDENTITY_INSERT [Purchase] OFF;
          
          SELECT
              [dbo_Customer].[FullName] [dbo_Customer_FullName],
              [dbo_Customer].[HomeAddressId] [dbo_Customer_HomeAddressId],
              [dbo_Customer].[BillingAddressId] [dbo_Customer_BillingAddressId],
              [dbo_Customer].[Id] [dbo_Customer_Id],
              [dbo_Address_1].[Town] [dbo_Address_Town_1],
              [dbo_Address_1].[Confirmed] [dbo_Address_Confirmed_1],
              [dbo_Address_1].[LastChanged] [dbo_Address_LastChanged_1],
              [dbo_Address_1].[ExternalId] [dbo_Address_ExternalId_1],
              [dbo_Address_1].[Id] [dbo_Address_Id_1],
              [dbo_Address_2].[Town] [dbo_Address_Town_2],
              [dbo_Address_2].[Confirmed] [dbo_Address_Confirmed_2],
              [dbo_Address_2].[LastChanged] [dbo_Address_LastChanged_2],
              [dbo_Address_2].[ExternalId] [dbo_Address_ExternalId_2],
              [dbo_Address_2].[Id] [dbo_Address_Id_2],
              [dbo_Purchase].[CustomerId] [dbo_Purchase_CustomerId],
              [dbo_Purchase].[ProductId] [dbo_Purchase_ProductId],
              [dbo_Purchase].[DeliveryId] [dbo_Purchase_DeliveryId],
              [dbo_Purchase].[PaymentMethodId] [dbo_Purchase_PaymentMethodId],
              [dbo_Purchase].[Id] [dbo_Purchase_Id],
              [dbo_Delivery].[Date] [dbo_Delivery_Date],
              [dbo_Delivery].[AddressId] [dbo_Delivery_AddressId],
              [dbo_Delivery].[Id] [dbo_Delivery_Id],
              [dbo_Address_3].[Town] [dbo_Address_Town_3],
              [dbo_Address_3].[Confirmed] [dbo_Address_Confirmed_3],
              [dbo_Address_3].[LastChanged] [dbo_Address_LastChanged_3],
              [dbo_Address_3].[ExternalId] [dbo_Address_ExternalId_3],
              [dbo_Address_3].[Id] [dbo_Address_Id_3],
              [dbo_Product].[Name] [dbo_Product_Name],
              [dbo_Product].[Price] [dbo_Product_Price],
              [dbo_Product].[Id] [dbo_Product_Id]
          FROM [dbo].[Customer] [dbo_Customer]
          LEFT JOIN [dbo].[Address] [dbo_Address_1] ON ([dbo_Customer].[HomeAddressId] = [dbo_Address_1].[Id])
          LEFT JOIN [dbo].[Address] [dbo_Address_2] ON ([dbo_Customer].[BillingAddressId] = [dbo_Address_2].[Id])
          LEFT JOIN [dbo].[Purchase] [dbo_Purchase] ON ([dbo_Customer].[Id] = [dbo_Purchase].[CustomerId])
          LEFT JOIN [dbo].[Delivery] [dbo_Delivery] ON ([dbo_Purchase].[DeliveryId] = [dbo_Delivery].[Id])
          LEFT JOIN [dbo].[Address] [dbo_Address_3] ON ([dbo_Delivery].[AddressId] = [dbo_Address_3].[Id])
          LEFT JOIN [dbo].[Product] [dbo_Product] ON ([dbo_Purchase].[ProductId] = [dbo_Product].[Id])
          WHERE ([dbo_Customer].[Id] = 3);

      • Trace Entries: 2
          ...LanguageAndORM.DatabaseAccess.SqlExecuter.ExecuteScript:
          public TItem ExecuteScript<TItem>(
              SqlTransaction transaction,
              /*WYSE*/IScript script,
              Converter<DbDataReader, TItem> converter,
              TraceTree parentTraceTree)
          {
              var traceTree = parentTraceTree.AddChild();
              traceTree.RecordCodeMethodExecution(MethodBase.GetCurrentMethod());
          
              using SqlCommand command = transaction.Connection.CreateCommand()
                  // A WYSE Render context determines certain (overridable) render settings.
                  // Here the context is SqlServer. More SQL flavors are supported.
                  // A WYSE render builder is used for stringifying
                  // the strongly typed SQL statements.
                  /*WYSE*/.For(script, _renderContextFactory.Create(), _createRenderBuilder);
              command.Transaction = transaction;
          
              try
              {
                  using DbDataReader reader = command.ExecuteReader();
          
                  var item = converter(reader);
          
                  traceTree.RecordDbCommandExecutionSuccess(
                      command.CommandText,
                      command.Parameters.ToDictionary(),
                      item);
          
                  return item;
              }
              catch (Exception exception)
              {
                  traceTree.RecordDbCommandExecutionFailure(
                      command.CommandText,
                      command.Parameters.ToDictionary(),
                      exception);
          
                  return default;
              }
          }

          Command Text:

          SET IDENTITY_INSERT [Address] ON;
          
          INSERT INTO [dbo].[Address]
          ([Id], [Town], [Confirmed], [LastChanged], [ExternalId])
          VALUES (22, 'NewDeliveryAddress', 0, NULL, NULL);
          
          SET IDENTITY_INSERT [Address] OFF;
          
          UPDATE [dbo].[Customer]
          SET [FullName] = 'Jane Doe',
              [HomeAddressId] = 21,
              [BillingAddressId] = NULL
          WHERE ([Id] = 3);
          
          UPDATE [dbo].[Purchase]
          SET [CustomerId] = 3,
              [ProductId] = 2,
              [DeliveryId] = 5,
              [PaymentMethodId] = 2
          WHERE ([Id] = 6);
          
          UPDATE [dbo].[Delivery]
          SET [Date] = '2025-06-06 00:00:00.000',
              [AddressId] = 22
          WHERE ([Id] = 5);
          
          DELETE FROM [dbo].[Purchase]
          WHERE ([Id] = 5);
          
          SELECT
              [dbo_Customer].[FullName] [dbo_Customer_FullName],
              [dbo_Customer].[HomeAddressId] [dbo_Customer_HomeAddressId],
              [dbo_Customer].[BillingAddressId] [dbo_Customer_BillingAddressId],
              [dbo_Customer].[Id] [dbo_Customer_Id],
              [dbo_Address_1].[Town] [dbo_Address_Town_1],
              [dbo_Address_1].[Confirmed] [dbo_Address_Confirmed_1],
              [dbo_Address_1].[LastChanged] [dbo_Address_LastChanged_1],
              [dbo_Address_1].[ExternalId] [dbo_Address_ExternalId_1],
              [dbo_Address_1].[Id] [dbo_Address_Id_1],
              [dbo_Address_2].[Town] [dbo_Address_Town_2],
              [dbo_Address_2].[Confirmed] [dbo_Address_Confirmed_2],
              [dbo_Address_2].[LastChanged] [dbo_Address_LastChanged_2],
              [dbo_Address_2].[ExternalId] [dbo_Address_ExternalId_2],
              [dbo_Address_2].[Id] [dbo_Address_Id_2],
              [dbo_Purchase].[CustomerId] [dbo_Purchase_CustomerId],
              [dbo_Purchase].[ProductId] [dbo_Purchase_ProductId],
              [dbo_Purchase].[DeliveryId] [dbo_Purchase_DeliveryId],
              [dbo_Purchase].[PaymentMethodId] [dbo_Purchase_PaymentMethodId],
              [dbo_Purchase].[Id] [dbo_Purchase_Id],
              [dbo_Delivery].[Date] [dbo_Delivery_Date],
              [dbo_Delivery].[AddressId] [dbo_Delivery_AddressId],
              [dbo_Delivery].[Id] [dbo_Delivery_Id],
              [dbo_Address_3].[Town] [dbo_Address_Town_3],
              [dbo_Address_3].[Confirmed] [dbo_Address_Confirmed_3],
              [dbo_Address_3].[LastChanged] [dbo_Address_LastChanged_3],
              [dbo_Address_3].[ExternalId] [dbo_Address_ExternalId_3],
              [dbo_Address_3].[Id] [dbo_Address_Id_3],
              [dbo_Product].[Name] [dbo_Product_Name],
              [dbo_Product].[Price] [dbo_Product_Price],
              [dbo_Product].[Id] [dbo_Product_Id]
          FROM [dbo].[Customer] [dbo_Customer]
          LEFT JOIN [dbo].[Address] [dbo_Address_1] ON ([dbo_Customer].[HomeAddressId] = [dbo_Address_1].[Id])
          LEFT JOIN [dbo].[Address] [dbo_Address_2] ON ([dbo_Customer].[BillingAddressId] = [dbo_Address_2].[Id])
          LEFT JOIN [dbo].[Purchase] [dbo_Purchase] ON ([dbo_Customer].[Id] = [dbo_Purchase].[CustomerId])
          LEFT JOIN [dbo].[Delivery] [dbo_Delivery] ON ([dbo_Purchase].[DeliveryId] = [dbo_Delivery].[Id])
          LEFT JOIN [dbo].[Address] [dbo_Address_3] ON ([dbo_Delivery].[AddressId] = [dbo_Address_3].[Id])
          LEFT JOIN [dbo].[Product] [dbo_Product] ON ([dbo_Purchase].[ProductId] = [dbo_Product].[Id])
          WHERE ([dbo_Customer].[Id] = 3);

Command Text:

SET IDENTITY_INSERT [Address] ON;

INSERT INTO [dbo].[Address]
([Id], [Town], [Confirmed], [LastChanged], [ExternalId])
VALUES (21, 'HomeAddress', 1, NULL, NULL);

SET IDENTITY_INSERT [Address] OFF;

SET IDENTITY_INSERT [Delivery] ON;

INSERT INTO [dbo].[Delivery]
([Id], [Date], [AddressId])
VALUES (4, '2025-05-23 00:00:00.000', 21);

SET IDENTITY_INSERT [Delivery] OFF;

SET IDENTITY_INSERT [Delivery] ON;

INSERT INTO [dbo].[Delivery]
([Id], [Date], [AddressId])
VALUES (5, '2025-06-06 00:00:00.000', 21);

SET IDENTITY_INSERT [Delivery] OFF;

SET IDENTITY_INSERT [Customer] ON;

INSERT INTO [dbo].[Customer]
([Id], [FullName], [HomeAddressId], [BillingAddressId])
VALUES (3, 'John Smith', 21, NULL);

SET IDENTITY_INSERT [Customer] OFF;

SET IDENTITY_INSERT [Purchase] ON;

INSERT INTO [dbo].[Purchase]
([Id], [CustomerId], [ProductId], [DeliveryId], [PaymentMethodId])
VALUES (5, 3, 1, 4, 1);

SET IDENTITY_INSERT [Purchase] OFF;

SET IDENTITY_INSERT [Purchase] ON;

INSERT INTO [dbo].[Purchase]
([Id], [CustomerId], [ProductId], [DeliveryId], [PaymentMethodId])
VALUES (6, 3, 2, 5, 3);

SET IDENTITY_INSERT [Purchase] OFF;

SELECT
    [dbo_Customer].[FullName] [dbo_Customer_FullName],
    [dbo_Customer].[HomeAddressId] [dbo_Customer_HomeAddressId],
    [dbo_Customer].[BillingAddressId] [dbo_Customer_BillingAddressId],
    [dbo_Customer].[Id] [dbo_Customer_Id],
    [dbo_Address_1].[Town] [dbo_Address_Town_1],
    [dbo_Address_1].[Confirmed] [dbo_Address_Confirmed_1],
    [dbo_Address_1].[LastChanged] [dbo_Address_LastChanged_1],
    [dbo_Address_1].[ExternalId] [dbo_Address_ExternalId_1],
    [dbo_Address_1].[Id] [dbo_Address_Id_1],
    [dbo_Address_2].[Town] [dbo_Address_Town_2],
    [dbo_Address_2].[Confirmed] [dbo_Address_Confirmed_2],
    [dbo_Address_2].[LastChanged] [dbo_Address_LastChanged_2],
    [dbo_Address_2].[ExternalId] [dbo_Address_ExternalId_2],
    [dbo_Address_2].[Id] [dbo_Address_Id_2],
    [dbo_Purchase].[CustomerId] [dbo_Purchase_CustomerId],
    [dbo_Purchase].[ProductId] [dbo_Purchase_ProductId],
    [dbo_Purchase].[DeliveryId] [dbo_Purchase_DeliveryId],
    [dbo_Purchase].[PaymentMethodId] [dbo_Purchase_PaymentMethodId],
    [dbo_Purchase].[Id] [dbo_Purchase_Id],
    [dbo_Delivery].[Date] [dbo_Delivery_Date],
    [dbo_Delivery].[AddressId] [dbo_Delivery_AddressId],
    [dbo_Delivery].[Id] [dbo_Delivery_Id],
    [dbo_Address_3].[Town] [dbo_Address_Town_3],
    [dbo_Address_3].[Confirmed] [dbo_Address_Confirmed_3],
    [dbo_Address_3].[LastChanged] [dbo_Address_LastChanged_3],
    [dbo_Address_3].[ExternalId] [dbo_Address_ExternalId_3],
    [dbo_Address_3].[Id] [dbo_Address_Id_3],
    [dbo_Product].[Name] [dbo_Product_Name],
    [dbo_Product].[Price] [dbo_Product_Price],
    [dbo_Product].[Id] [dbo_Product_Id]
FROM [dbo].[Customer] [dbo_Customer]
LEFT JOIN [dbo].[Address] [dbo_Address_1] ON ([dbo_Customer].[HomeAddressId] = [dbo_Address_1].[Id])
LEFT JOIN [dbo].[Address] [dbo_Address_2] ON ([dbo_Customer].[BillingAddressId] = [dbo_Address_2].[Id])
LEFT JOIN [dbo].[Purchase] [dbo_Purchase] ON ([dbo_Customer].[Id] = [dbo_Purchase].[CustomerId])
LEFT JOIN [dbo].[Delivery] [dbo_Delivery] ON ([dbo_Purchase].[DeliveryId] = [dbo_Delivery].[Id])
LEFT JOIN [dbo].[Address] [dbo_Address_3] ON ([dbo_Delivery].[AddressId] = [dbo_Address_3].[Id])
LEFT JOIN [dbo].[Product] [dbo_Product] ON ([dbo_Purchase].[ProductId] = [dbo_Product].[Id])
WHERE ([dbo_Customer].[Id] = 3);

Command Text:

SET IDENTITY_INSERT [Address] ON;

INSERT INTO [dbo].[Address]
([Id], [Town], [Confirmed], [LastChanged], [ExternalId])
VALUES (22, 'NewDeliveryAddress', 0, NULL, NULL);

SET IDENTITY_INSERT [Address] OFF;

UPDATE [dbo].[Customer]
SET [FullName] = 'Jane Doe',
    [HomeAddressId] = 21,
    [BillingAddressId] = NULL
WHERE ([Id] = 3);

UPDATE [dbo].[Purchase]
SET [CustomerId] = 3,
    [ProductId] = 2,
    [DeliveryId] = 5,
    [PaymentMethodId] = 2
WHERE ([Id] = 6);

UPDATE [dbo].[Delivery]
SET [Date] = '2025-06-06 00:00:00.000',
    [AddressId] = 22
WHERE ([Id] = 5);

DELETE FROM [dbo].[Purchase]
WHERE ([Id] = 5);

SELECT
    [dbo_Customer].[FullName] [dbo_Customer_FullName],
    [dbo_Customer].[HomeAddressId] [dbo_Customer_HomeAddressId],
    [dbo_Customer].[BillingAddressId] [dbo_Customer_BillingAddressId],
    [dbo_Customer].[Id] [dbo_Customer_Id],
    [dbo_Address_1].[Town] [dbo_Address_Town_1],
    [dbo_Address_1].[Confirmed] [dbo_Address_Confirmed_1],
    [dbo_Address_1].[LastChanged] [dbo_Address_LastChanged_1],
    [dbo_Address_1].[ExternalId] [dbo_Address_ExternalId_1],
    [dbo_Address_1].[Id] [dbo_Address_Id_1],
    [dbo_Address_2].[Town] [dbo_Address_Town_2],
    [dbo_Address_2].[Confirmed] [dbo_Address_Confirmed_2],
    [dbo_Address_2].[LastChanged] [dbo_Address_LastChanged_2],
    [dbo_Address_2].[ExternalId] [dbo_Address_ExternalId_2],
    [dbo_Address_2].[Id] [dbo_Address_Id_2],
    [dbo_Purchase].[CustomerId] [dbo_Purchase_CustomerId],
    [dbo_Purchase].[ProductId] [dbo_Purchase_ProductId],
    [dbo_Purchase].[DeliveryId] [dbo_Purchase_DeliveryId],
    [dbo_Purchase].[PaymentMethodId] [dbo_Purchase_PaymentMethodId],
    [dbo_Purchase].[Id] [dbo_Purchase_Id],
    [dbo_Delivery].[Date] [dbo_Delivery_Date],
    [dbo_Delivery].[AddressId] [dbo_Delivery_AddressId],
    [dbo_Delivery].[Id] [dbo_Delivery_Id],
    [dbo_Address_3].[Town] [dbo_Address_Town_3],
    [dbo_Address_3].[Confirmed] [dbo_Address_Confirmed_3],
    [dbo_Address_3].[LastChanged] [dbo_Address_LastChanged_3],
    [dbo_Address_3].[ExternalId] [dbo_Address_ExternalId_3],
    [dbo_Address_3].[Id] [dbo_Address_Id_3],
    [dbo_Product].[Name] [dbo_Product_Name],
    [dbo_Product].[Price] [dbo_Product_Price],
    [dbo_Product].[Id] [dbo_Product_Id]
FROM [dbo].[Customer] [dbo_Customer]
LEFT JOIN [dbo].[Address] [dbo_Address_1] ON ([dbo_Customer].[HomeAddressId] = [dbo_Address_1].[Id])
LEFT JOIN [dbo].[Address] [dbo_Address_2] ON ([dbo_Customer].[BillingAddressId] = [dbo_Address_2].[Id])
LEFT JOIN [dbo].[Purchase] [dbo_Purchase] ON ([dbo_Customer].[Id] = [dbo_Purchase].[CustomerId])
LEFT JOIN [dbo].[Delivery] [dbo_Delivery] ON ([dbo_Purchase].[DeliveryId] = [dbo_Delivery].[Id])
LEFT JOIN [dbo].[Address] [dbo_Address_3] ON ([dbo_Delivery].[AddressId] = [dbo_Address_3].[Id])
LEFT JOIN [dbo].[Product] [dbo_Product] ON ([dbo_Purchase].[ProductId] = [dbo_Product].[Id])
WHERE ([dbo_Customer].[Id] = 3);

There is no result available.