Blogs

Gary's Blog

XPO - Stored Procedure Support coming in V2010 Vol 2 (Part 2)

     

You’ll recall in my previous post on this topic I told you that there would be two ways of working with stored procedures in the forthcoming 2010.2 release, those were:

  1. Direct calling of existing stored procedures and the ability to handle returned data;
  2. Mapping of persistent classes on views in the database with help of INSTEAD-OF triggers and stored procedures.

I then went on to demonstrate how to use the first version. Well guess what? I’m back, and this time we’re going to take a look at that second variant, which works by mapping persistent classes to views in the database, which in turn query data from related tables in the database. Clearly, it’s not possible to add, edit or delete records from a database view and so to solve this problem, we’ll use INSTEAD-OF triggers to redirect the required operations to the stored procedures. To do this we’ll have to perform the following set of tasks for each database table:

  1. Create a view.
  2. Map the persistent class to the corresponding View;
  3. Create INSTEAD-OF triggers for INSERT, UPDATE and DELETE functionality.
  4. Create stored procedure for INSERT, UPDATE and DELETE functionality.

In the first blog post I demonstrated how to use the Persistent Classes Wizard to get started with stored procedures. Here we will use the same technique, with one difference. In the Generating Persistent Classes for an existing Database dialog, we will check the Generate views and stored procedures for tables access button.

Generate Persistent Classes for an Existing Database Wizard

The remaining steps are the same as in the previous post. As a result, the wizard will generate the code shown in the first post as well as the DDL code to generate views, triggers and stored procedures in the database.

For example, this code generates an EmployeeSplit_xpoView view:

CREATE VIEW [EmployeeSplit_xpoView] AS
    SELECT
        [ID],
        [Extension],
        [PhotoPath]
    FROM [EmployeeSplit]
GO
CREATE PROCEDURE [sp_EmployeeSplit_xpoView_insert]
    @ID int,
    @Extension nvarchar(4),
    @PhotoPath nvarchar(255)
AS
BEGIN
    BEGIN TRY
        INSERT INTO [EmployeeSplit](
            [ID],
            [Extension],
            [PhotoPath]
        )
        VALUES(
            @ID,
            @Extension,
            @PhotoPath
        );
    END TRY
    BEGIN CATCH
        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;
        SELECT @ErrorMessage = ERROR_MESSAGE(),
            @ErrorSeverity = ERROR_SEVERITY(),
            @ErrorState = ERROR_STATE();
        RAISERROR(
            @ErrorMessage,
            @ErrorSeverity,
            @ErrorState
        );
    END CATCH
END
GO
CREATE PROCEDURE [sp_EmployeeSplit_xpoView_update]
    @ID int,
    @old_Extension nvarchar(4),
    @Extension nvarchar(4),
    @old_PhotoPath nvarchar(255),
    @PhotoPath nvarchar(255)
AS
    UPDATE [EmployeeSplit] SET
        [Extension]=@Extension,
        [PhotoPath]=@PhotoPath
    WHERE
        [ID] = @ID
GO
CREATE PROCEDURE [sp_EmployeeSplit_xpoView_delete]
    @ID int,
    @old_Extension nvarchar(4),
    @old_PhotoPath nvarchar(255)
AS
    DELETE FROM [EmployeeSplit] WHERE
        [ID] = @ID
GO

And this code generates the respective triggers.

CREATE TRIGGER [t_EmployeeSplit_xpoView_insert]
ON [EmployeeSplit_xpoView]
INSTEAD OF INSERT AS
BEGIN
    DECLARE @cur CURSOR
    SET @cur = CURSOR FOR
        SELECT
            [ID],
            [Extension],
            [PhotoPath]
        FROM inserted
    OPEN @cur
    DECLARE @ID int
    DECLARE @Extension nvarchar(4)
    DECLARE @PhotoPath nvarchar(255)
    FETCH NEXT FROM @cur INTO
        @ID,
        @Extension,
        @PhotoPath
    WHILE(@@fetch_status <> -1)
    BEGIN
        EXEC [sp_EmployeeSplit_xpoView_insert]
            @ID,
            @Extension,
            @PhotoPath
        FETCH NEXT FROM @cur INTO
            @ID,
            @Extension,
            @PhotoPath
    END
    CLOSE @cur
    DEALLOCATE @cur
END
GO
CREATE TRIGGER [t_EmployeeSplit_xpoView_update]
ON [EmployeeSplit_xpoView]
INSTEAD OF UPDATE AS
BEGIN
    DECLARE @cur CURSOR
    SET @cur = CURSOR FOR
        SELECT
            i.[ID],
            d.[Extension] as [old_Extension],
            i.[Extension],
            d.[PhotoPath] as [old_PhotoPath],
            i.[PhotoPath]
        FROM
            inserted i
            INNER JOIN
            deleted d
            ON
                i.[ID] = d.[ID]
    OPEN @cur
    DECLARE @ID int
    DECLARE @old_Extension nvarchar(4)
    DECLARE @Extension nvarchar(4)
    DECLARE @old_PhotoPath nvarchar(255)
    DECLARE @PhotoPath nvarchar(255)
    FETCH NEXT FROM @cur INTO
        @ID,
        @old_Extension,
        @Extension,
        @old_PhotoPath,
        @PhotoPath
    WHILE(@@fetch_status <> -1)
    BEGIN
        EXEC [sp_EmployeeSplit_xpoView_update]
            @ID,
            @old_Extension,
            @Extension,
            @old_PhotoPath,
            @PhotoPath
        FETCH NEXT FROM @cur INTO
            @ID,
            @old_Extension,
            @Extension,
            @old_PhotoPath,
            @PhotoPath
    END
    CLOSE @cur
    DEALLOCATE @cur
END
GO
CREATE TRIGGER [t_EmployeeSplit_xpoView_delete]
ON [EmployeeSplit_xpoView]
INSTEAD OF DELETE AS
BEGIN
    DECLARE @cur CURSOR
    SET @cur = CURSOR FOR
        SELECT
            [ID],
            [Extension],
            [PhotoPath]
        FROM deleted
    OPEN @cur
    DECLARE @ID int
    DECLARE @Extension nvarchar(4)
    DECLARE @PhotoPath nvarchar(255)
    FETCH NEXT FROM @cur INTO
        @ID,
        @Extension,
        @PhotoPath
    WHILE(@@fetch_status <> -1)
    BEGIN
        EXEC [sp_EmployeeSplit_xpoView_delete]
            @ID,
            @Extension,
            @PhotoPath
        FETCH NEXT FROM @cur INTO
            @ID,
            @Extension,
            @PhotoPath
    END
    CLOSE @cur
    DEALLOCATE @cur
END
GO


DROP TRIGGER [t_EmployeeSplit_xpoView_delete]
GO
DROP TRIGGER [t_EmployeeSplit_xpoView_update]
GO
DROP TRIGGER [t_EmployeeSplit_xpoView_insert]
GO
DROP PROCEDURE [sp_EmployeeSplit_xpoView_delete]
GO
DROP PROCEDURE [sp_EmployeeSplit_xpoView_update]
GO
DROP PROCEDURE [sp_EmployeeSplit_xpoView_insert]
GO
DROP VIEW [EmployeeSplit_xpoView]
GO

In addition to the above generated code, the corresponding persistent classes will be decorated with the PersistentAttribute, mapping them to the corresponding views in the database:

using System;
using DevExpress.Xpo;
namespace Northwind {

    [Persistent("EmployeeSplit_xpoView")]
    public class EmployeeSplit : XPLiteObject {
        int fID;
        [Key]
        public int ID {
            get { return fID; }
            set { SetPropertyValue<int>("ID", ref fID, value); }
        }
        string fExtension;
        [Size(4)]
        public string Extension {
            get { return fExtension; }
            set { SetPropertyValue<string>("Extension", ref fExtension, value); }
        }
        string fPhotoPath;
        [Size(255)]
        public string PhotoPath {
            get { return fPhotoPath; }
            set { SetPropertyValue<string>("PhotoPath", ref fPhotoPath, value); }
        }
        public EmployeeSplit(Session session) : base(session) { }
        public EmployeeSplit() : base(Session.DefaultSession) { }
        public override void AfterConstruction() { base.AfterConstruction(); }
    }

    [NonPersistent]
    public class CustOrdersOrders : PersistentBase {
        int fOrderID;
        public int OrderID {
            get { return fOrderID; }
            set { SetPropertyValue<int>("OrderID", ref fOrderID, value); }
        }
        DateTime fOrderDate;
        public DateTime OrderDate {
            get { return fOrderDate; }
            set { SetPropertyValue<DateTime>("OrderDate", ref fOrderDate, value); }
        }
        DateTime fRequiredDate;
        public DateTime RequiredDate {
            get { return fRequiredDate; }
            set { SetPropertyValue<DateTime>("RequiredDate", ref fRequiredDate, value); }
        }
        public CustOrdersOrders(Session session) : base(session) { }
        public CustOrdersOrders() : base(Session.DefaultSession) { }
        public override void AfterConstruction() { base.AfterConstruction(); }
    }
    public static class NorthwindSprocHelper {

        public static DevExpress.Xpo.DB.SelectedData ExecCustOrdersOrders(Session session, string CustomerID){
            return session.ExecuteSproc("CustOrdersOrders", CustomerID);
        }

        static LoadDataMemberOrderItem[] CustOrdersOrdersOrderArray = {new LoadDataMemberOrderItem(0, "OrderID"), new LoadDataMemberOrderItem(1, "OrderDate"), new LoadDataMemberOrderItem(2, "RequiredDate")};

        public static System.Collections.Generic.ICollection<CustOrdersOrders> ExecCustOrdersOrdersIntoObjects(Session session, string CustomerID){
            return session.GetObjectsFromSproc<CustOrdersOrders>(CustOrdersOrdersOrderArray, "CustOrdersOrders", CustomerID);
        }

        public static XPDataView ExecCustOrdersOrdersIntoDataView(Session session, string CustomerID){
            DevExpress.Xpo.DB.SelectedData sprocData = session.ExecuteSproc("CustOrdersOrders", CustomerID);
            return new XPDataView(session.Dictionary, session.GetClassInfo(typeof(CustOrdersOrders)), CustOrdersOrdersOrderArray, sprocData);
        }
        public static void ExecCustOrdersOrdersIntoDataView(XPDataView dataView, Session session, string CustomerID){
            DevExpress.Xpo.DB.SelectedData sprocData = session.ExecuteSproc("CustOrdersOrders", CustomerID);
            dataView.PopulatePropertiesOrdered(session.GetClassInfo(typeof(CustOrdersOrders)), CustOrdersOrdersOrderArray);
            dataView.LoadOrderedData(CustOrdersOrdersOrderArray, sprocData);
        }
    }
}

Well that’s it for this post, until next time, happy XPO’ing Smile

Published Sep 28 2010, 05:41 PM by Gary Short (DevExpress)
Bookmark and Share

Comments

 

CARLOS MELGAR said:

Nice! Will this work with XAF too?? I hope so!!!

September 28, 2010 10:02 PM
 

Graeme Davies said:

Will the XPO Stored Procedure Support  work for the other XPO supported Databases eg Postgres, MySQL, Oracle etc

September 30, 2010 12:54 AM
 

Dennis (DevExpress Support) said:

Thanks for your feedback, guys!

@Carlos: Absolutely any feature provided in XPO can be used and can work in an XAF application. Let us know in case of any difficulties (if found).

@Graeme: When working on this feature, we tried to support it for as many databases as possible, and often needed write some workarounds to bypass native limitations of existing databases. Anyway, the feature described in this part will work only with MS SQL Server and Oracle. We are afraid it is not well supported by other databases.

The feature described in the first part of the blog about stored procedures, will work fine with the MSSql, Oracle, ASA, PostgreSQL, Ase databases. The FireBird, DB2, MySQL and Advantage databases have some restrictions with regard to generating resulting set columns, when Access, MSSQLCE, SQLite do not even support getting the list of stored procedures...

However, you can call a stored procedure using the corresponding methods of the Session class when working with all databases, except for the MSSQLCE, SQLite.

October 1, 2010 12:20 PM
 

Alex van den Elshout said:

I must be missing something here, but what is new about this? We have been mapping views to persistent classes since version 6.x. The views call the triggers which in turn call the stored procedures which is all done by the database. XPO has not much to do with this. So what is the new bit I do not see?

November 22, 2010 2:44 AM
 

Roman Rak said:

>>The feature described in the first part of the blog about >>stored procedures, will work fine with the MSSql, Oracle, >>ASA, PostgreSQL, Ase databases. The FireBird, DB2, >>MySQL and Advantage databases have some restrictions >>with regard to generating resulting set columns, when >>Access, MSSQLCE, SQLite do not even support getting the >>list of stored procedures...

And you plan support for VistaDB 4 stored procedures ?

Thank you

Roman

December 2, 2010 2:55 AM
 

aixi he said:

i s this also supported webservice too?

December 2, 2010 6:42 AM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.