Методы CreateMethodCallQuery() и ExecuteMethodCall()

46

»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ

CreateMethodCallQuery()

Первое, что нужно знать о методе CreateMethodCallQuery — это защищенный (protected) метод. Значит, он не может быть вызван в коде приложения, и чтобы получить такую возможность, понадобится унаследовать свой класс от DataContext.

Метод CreateMethodCallQuery используется для вызова возвращающих табличные значения пользовательских функций. Метод ExecuteMethodCall применяется для вызова пользовательских функций, возвращающих скалярные значения, и он рассматривается позже.

Метод CreateMethodCallQuery имеет один прототип, описанный ниже:

protected internal IQueryable<T> CreateMethodCallQuery<T>( object instance,
         System.Reflection.MethodInfo methodInfo, 
         params object[] parameters)

Метод CreateMethodCallQuery принимает ссылку на объект DataContext или [Your]DataContext, членом которого является метод, вызывающий CreateMethodCallQuery, объект MethodInfo для этого вызывающего метода и массив params параметров для возвращающей табличное значение пользовательской функции.

Поскольку метод CreateMethodCallQuery является protected и может быть вызван только из класса DataContext или его наследника, вместо представления примера, который действительно вызывает метод CreateMethodCallQuery, рассмотрим метод, сгенерированный SQLMetal для пользовательской функции, которая возвращает табличное значение ProductsUnderThisUnitPrice из расширенной базы данных Northwind:

[Function(Name="dbo.ProductsUnderThisUnitPrice", IsComposable=true)]
public IQueryable<ProductsUnderThisUnitPriceResult> ProductsUnderThisUnitPrice(
                  [Parameter(DbType="Money")] System.Nullable<decimal> price)
{
			return this.CreateMethodCallQuery<ProductsUnderThisUnitPriceResult>(
                  this, 
                  ((MethodInfo)(MethodInfo.GetCurrentMethod())), price);
}

В приведенном выше коде видно, что метод ProductsUnderThisUnitPrice снабжен атрибутом Function, поэтому известно, что он собирается вызывать либо хранимую процедуру, либо определяемую пользователем функцию по имени ProductsUnderThisUnitPrice. Поскольку свойство IsComposable атрибута установлено в true, это определяемая пользователем функция, а не хранимая процедура. Так как сгенерированный код вызывает метод CreateMethodCallQuery, это значит, что указанная пользовательская функция ProductsUnderThisUnitPrice возвращает табличное значение, а не скалярное.

Из аргументов, переданных методу CreateMethodCallQuery, первым аргументом является ссылка на производный от DataContext класс, сгенерированный SQLMetal. Второй аргумент — объект MethodInfo текущего метода. Это позволит методу CreateMethodCallQuery получить доступ к атрибутам, так что он будет иметь необходимую информацию для вызова определяемой пользователем функции, возвращающей табличное значение, такую как ее имя. Третий аргумент, переданный методу CreateMethodCallQuery — это единственный параметр, принимаемый указанной пользовательской функцией, которым в данном случае является цена.

Значение, возвращенное вызовом метода CreateMethodCallQuery, будет возвращено методом ProductsUnderThisUnitPrice, и им будет последовательность объектов РroductsUnderThisUnitPriceResult. Инструмент SQLMetal также сгенерировал класс ProductsUnderThisUnitPriceResult.

Код, о котором шла речь, показывает, как вызывать метод CreateMethodCallQuery, но чтобы задать некоторый контекст, давайте рассмотрим пример вызова сгенерированного метода ProductsUnderThisUnitPriceResult в действии:

// Используйте свое подключение
            Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;
                       Initial Catalog=C:\NORTHWIND.MDF;
                       Integrated Security=True");

            IQueryable<ProductsUnderThisUnitPriceResult> results =
                db.ProductsUnderThisUnitPrice(new Decimal(5.50));

            foreach (ProductsUnderThisUnitPriceResult prod in results)
                Console.WriteLine("{0} - {1:C}",prod.ProductName, prod.UnitPrice);

Вот результат работы этого примера:

Пример вызова метода ProductsUnderThisUnitPrice

ExecuteMethodCall()

Первое, что нужно знать о методе ExecuteMethodCall — то, что он является защищенным (protected). Это значит, что вызывать этот метод в коде приложения нельзя, а для этого понадобится унаследовать свой класс от DataContext.

Метод ExecuteMethodCall используется для вызова хранимых процедур и пользовательских функций, возвращающих скалярные значения.

Метод ExecuteMethodCall имеет один прототип, описанный ниже:

protected internal IExecuteResult ExecuteMethodCall(object instance,
     System.Reflection.MethodInfo methodInfo, 
     params object[] parameters)

Методу ExecuteMethodCall передается ссылка на объект DataContext или [Your]DataContext, членом которого является метод, вызвавший ExecuteMethodCall, объект MethodlInfo для вызова метода и массив params параметров хранимой процедуры или пользовательской функции, возвращающей скалярное значение.

Обратите внимание, что поскольку необходимо передавать объект MethodInfo, метод должен быть оснащен соответствующим атрибутом хранимой процедуры или пользовательской функции, с соответствующими свойствами атрибута. LINQ to SQL затем использует объект MethodInfo для доступа к атрибуту метода Function, чтобы получить имя хранимой процедуры или пользовательской функции, возвращающей скалярное значение. Также он использует объект MethodInfo для получения имен и типов параметров.

Метод ExecuteMethodCall возвращает объект, реализующий интерфейс IExecuteResult.

Если для генерации сущностных классов применяется инструмент SQLMetal, он создаст методы сущностных классов, которые вызовут ExecuteMethodCall для всех хранимых процедур базы данных, если указана опция /sprocs, и для пользовательских функций базы данных — если указана опция /function.

Прежде чем ознакомиться с кодом первого примера, рассмотрим метод по имени CustomersCountByRegion, который SQLMetal сгенерировал для вызова хранимой процедуры базы данных CustomersCountByRegion. Вот как выглядит этот генерированный метод:

[Function(Name="dbo.Customers Count By Region")]
[return: Parameter(DbType="Int")]
public int CustomersCountByRegion([Parameter(DbType="NVarChar(15)")] string param1)
{
		IExecuteResult result = this.ExecuteMethodCall(this, 
            ((MethodInfo)(MethodInfo.GetCurrentMethod())), param1);
		return ((int)(result.ReturnValue));
}

Как видите, методу CustomersCountByRegion передается параметр string, который затем передается в качестве параметра методу ExecuteMethodCall, который, в свою очередь, передает его хранимой процедуре Customers Count By Region.

Метод ExecuteMethodCall возвращает переменную, реализующую IExecuteResult. Чтобы получить целочисленное возвращаемое значение, метод CustomersCountByRegion просто ссылается на свойство возвращенного объекта ReturnValue и выполняет приведение его к int. Теперь давайте заглянем на следующий пример, в котором приведен некоторый код, вызывающий сгенерированный метод CustomersCountByRegion:

// Используйте свое подключение
            Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;
                       Initial Catalog=C:\NORTHWIND.MDF;
                       Integrated Security=True");

            int rc = db.CustomersCountByRegion("WA");
            Console.WriteLine("\n  В регионе 'WA' находится {0} заказчика.",rc);

Это очень тривиальный пример, без каких-либо сюрпризов. Вот результат:

Пример вызова сгенерированного метода CustomersCountByRegion

Теперь посмотрим, как вызывать хранимую процедуру, которая возвращает выходной параметр. Опять-таки, имея сгенерированные SQLMetal сущностные классы для базы данных Northwind, проанализируем метод CustOrderTotal, сгенерированный для вызова хранимой процедуры CustOrderTotal:

[Function(Name="dbo.CustOrderTotal")]
[return: Parameter(DbType="Int")]
public int CustOrderTotal(
       [Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID, 
       [Parameter(Name="TotalSales", DbType="Money")] ref System.Nullable<decimal> totalSales)
	{
			IExecuteResult result = this.ExecuteMethodCall(this, 
                    ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID, totalSales);
			totalSales = ((System.Nullable<decimal>)(result.GetParameterValue(1)));
			return ((int)(result.ReturnValue));
	}

Обратите внимание, что во втором параметре метода CustOrderTotal — totalSales — указано ключевое слово ref. Это намек на то, что хранимая процедура собирается возвращать это значение. Для того чтобы получить значение после вызова метода ExecuteMethodCall, код вызывает метод GetParameterValue на возвращенном объекте, реализующем IExecuteResult, и передает ему 1, поскольку интересует второй параметр. Код ниже вызывает метод CustOrderTotal:

// Используйте свое подключение
            Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;
                       Initial Catalog=C:\NORTHWIND.MDF;
                       Integrated Security=True");

            decimal? totalSales = 0;

            int rc = db.CustOrderTotal("LAZYK", ref totalSales);
            Console.WriteLine("\n  Заказчик LAZYK имеет общий объем продаж на сумму {0}$.", totalSales);
Пройди тесты
Лучший чат для C# программистов