Добавление записи и получение Primary Key за один приём.
От: Блудов Павел Россия  
Дата: 11.08.06 11:46
Оценка:

Введение

Очень часто хочется создать из ничего объект, сохранить его на сервере и стразу же получить с сервера все автоматически генерируемые поля. Например, identities.

Примерно вот так:
Person p = new Person("John", "Pupkin");
SomeMagicToInsertIntoTablePerson(p);
// Вот здесь p.ID уже имеет осмысленное и готовое к употреблению значение.


Реализация на низком уровне

На низком уровне всё просто. Можно, например написать вот такую хранимую процедуру (MsSql):
CREATE Procedure Person_Insert
    @FirstName  nvarchar(50),
    @LastName   nvarchar(50),
    @MiddleName nvarchar(50),
    @Gender     char(1)
AS

INSERT INTO Person
    ( LastName,  FirstName,  MiddleName,  Gender)
VALUES
    (@LastName, @FirstName, @MiddleName, @Gender)

SELECT Cast(SCOPE_IDENTITY() as int) PersonID

и вызывать её таким вот образом:
using (DbManager db = new DbManager())
{
    db
        .SetSpCommand("Person_Insert", db.CreateParameters(e))
        .ExecuteObject(e);
}

Т.е. с объекта типа Person создаются параметры, вызывается хранимая процедура, а возвращаемые поля (в данном случае PersonID) автоматически мапятся на тот же самый объект. Этот подход хорош тем, что в таком виде хранимую процедуру можно легко заменить на простой запрос и использовать с базой данных не поддерживающей хранимые проуедуры (Access, SqlCe).

А можно воспользоваться возвращаемыми параметрами (Oracle):
CREATE OR REPLACE 
PROCEDURE Person_Insert
    ( pFirstName  IN NVARCHAR2
    , pLastName   IN NVARCHAR2
    , pMiddleName IN NVARCHAR2
    , pGender     IN CHAR
    , pPersonID   OUT NUMBER
    ) IS
BEGIN
INSERT INTO Person
    ( LastName,  FirstName,  MiddleName,  Gender)
VALUES
    (pLastName, pFirstName, pMiddleName, pGender)
RETURNING
    PersonID
INTO
    pPersonID;
END;

и вызывать её вот таким образом:
using (DbManager db = new DbManager())
{
    db
        .SetSpCommand("Person_Insert", db.CreateParameters(e, new string[]{"PersonID"}, null))
        .ExecuteNonQuery();
        
    db.MapOutputParameters(e);
}

И это будет выполняться гораздо быстрее, чем предыдущий способ, так как параметры уже вернулись с сервера на выходе из ExecuteNonQuery, а чтение из DataReader'а в предыдущем примере требует ещё одного обращения к серверу. Кроме того, инициализация чтения из DataReader'а в BLToolkit'е довольно сложная процедура, в то время как чтение значения из IDbDataParameter'а это простейшая операция.

Кроме того, есть ещё один, немного экзотический вариант. Если возвращается ровно одно значение, то его можно вернуть из хранимой процедуры в качестве RETURN_VALUE (MsSql):
CREATE FUNCTION Person_Insert
    @FirstName  nvarchar(50),
    @LastName   nvarchar(50),
    @MiddleName nvarchar(50),
    @Gender     char(1)
RETURNS int
AS

INSERT INTO Person
    ( LastName,  FirstName,  MiddleName,  Gender)
VALUES
    (@LastName, @FirstName, @MiddleName, @Gender)

RETURN Cast(SCOPE_IDENTITY() as int)

В этом случае использовать это можно так:
using (DbManager db = new DbManager())
{
    db
        .SetSpCommand("Person_Insert", db.CreateParameters(e))
        .ExecuteNonQuery();
        
    db.MapOutputParameters(e, "PersonID");
}


Отличия от предыдущего варианта исключительно косметические.

Реализация на высоком (DataAccessor) уровне

А здесь все весьма неочевидно. Если для DataAccessorBuider можно будет реализовать все эти варианты, введя нужное количество атрибутов, то в самом DataAccessor'е для CRUDL операций нужно что-то одно. Либо реализовывать их всех с разными суффиксами. Вобщем, полный
... << RSDN@Home 1.2.0 alpha rev. 642>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.