12

Использование технологии LinqToSql для прямого доступа к данным 1С:Предприятие

by Elisy 17. October 2009 08:19

Статья описывает методику генерации dbml-файла для данных СУБД SQLServer 1С:Предприятие и последующего прямого обращения к ним. Dbml-файл далее с успехом может использоваться в ASP.Net 3.5 для отображения актуальных данных. Это малоисследованный способ, который может иметь как невыявленные недостатки, так и достоинства. Будьте предельно внимательны при изменении данных, так как ошибки могут сделать конфигурацию 1С неработоспособной.

Все началось с задачи: реализовать веб-сайт – электронный каталог товаров. Данные в веб-сайт должны поступать из 1С-конфигурации Управление Торговлей. Управление Торговлей работает в клиент-серверном варианте под управлением СУБД MS SQL.

Выбор системы доступа к данным

1С можно приобщить к веб-технологиям следующими способами:

  • Используя продукты: web-extension, 1С:Битрикс,
  • Опубликовав родной для 1С веб-сервис,
  • Сделав экспорт в промежуточное хранилище, родное для веб-сайта, например, в Access,
  • Прямым доступом к данным: запрос к СКЛСерверу или технология Linq;

Web-расширение для 1С и 1С:Битрикс – это платные продукты, написанные специально для этого. Боюсь ошибиться, но их ядра построены на WebForms, что может существенно затруднить использование в среде Asp.Net MVC. Их нет смысла использовать для достаточно простых задач.

Публикация веб-сервиса 1С требует особых знаний конфигурирования 1С. Кроме того, хорошим тоном при публиации веб-сервисов считается использование SSL-протокола, а это дополнительные проблемы получения сертификатов, конфигурации и доступа.

Третий вариант промежуточного хранилища лишит сайт прямого доступа к данным, а значит "свежести" этих данных.

Вариант прямого доступа к данным через SQLServer понравился больше всех. Решено было проработать вначале его. Особых забот относительно выбора технологии реализации не возникало – это Asp.Net, если возможно Asp.Net MVC.

Выбор способа прямого доступа к данным 1С

Как ни странно, но и при прямом доступе к данным SQL Server есть выбор в способе. Есть и связанные с 1С проблемы, которые необходимо решать.

Главная проблема в случае 1С – нет стандарта относительно наименования таблиц и полей в базе данных 1С. Если кто заглядывал внутрь базы данных, то мог увидеть множество таблиц с непонятными названиями вида:

 

Пример структуры базы данных 1С:Предприятие

Это обстоятельство не дает при доступе к данным использовать SQL-выражения, генерируемые в строках, и вызов их для получения DataReader и DataTable-объектов.

Однажды сгенерированный запрос может потерять смысл и не работать при значимом обновлении конфигурации 1C или на конфигурации другого предприятия. В нашем случае запрос, объединяющий справочники Номенклатура, ЕдиницыИзмрения и регистр накопления ТоварыНаСкладах, может выглядеть так:

SELECT TOP 5 NomenclatureRef, 
Nomenclature._Description, Nomenclature._Fld720RRef AS UnitRef,
Units._Description,
SUM(_Fld6354) AS Quantity, MAX(Units._Fld501) AS Weight
FROM
(
SELECT _Period, _Fld6349RRef AS WarehouseRef, _Fld6350RRef AS NomenclatureRef, _Fld6354
FROM _AccumReg6348
WHERE _Active = 1 AND _Period BETWEEN DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) AND GetDate()
UNION ALL
SELECT _Period, _Fld6349RRef AS WarehouseRef, _Fld6350RRef AS NomenclatureRef, _Fld6354
FROM _AccumRegTotals6356
WHERE _Period = DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)
) AS Balance
INNER JOIN _Reference53 AS Nomenclature ON Nomenclature._Marked = 0 AND NomenclatureRef = Nomenclature._IDRRef AND Nomenclature._IDRRef IN (SELECT _IDRRef FROM ##tt)
INNER JOIN _Reference35 AS Units ON Units._Marked = 0 AND Nomenclature._Fld720RRef = Units._IDRRef
GROUP BY NomenclatureRef,
Nomenclature._Description, Nomenclature._Fld720RRef,
Units._Description

А что, если в следующий раз 1С решит поменять имя регистра _AccumReg6348 на, например, _AccumReg7000 или поменяет где-нибудь название поля. Весь запрос перестанет работать.

При рассмотрении способа доступа LinqToSql проблема остается – меняющиеся названия объектов БД, но ее значимость можно уменьшить, сгенерировав dbml-файл из 1С, присвоив названия классам из метаданных конфигурации 1С. Если при обновлении конфигурации изменятся имена таблиц или полей БД, то необходимо будет только заново сгенерить dbml-файл и поместить его в App_Code-каталог без изменения кода классов программы.

Внешняя обработка Elisy.LinqTo1CSql.81.epf

На этом этапе появляется необходимость во внешней обработке, которая объединит метаданные 1С и свойства объектов СУБД, генерирует на выходе dbml-файл.

Для генерации dbml-файла была создана обработка Elisy.LinqTo1CSql.81.epf, которая в дальнейшем войдет в состав продукта Elisy .Net Bridge. Здесь находится ее альфа-версия, генерящая dbml-описания жестко для справочников Номенклатура, ЕдиницыИзмерения и регистра накопления ТоварыНаСкладах. Обработка требует заргистрированный внешний компонент Elisy.NetBridge.dll (версии 2.3). Это внешний вид обработки:

Обработка Elisy.LinqTo1CSql.81.epf для генерации Dbml-файла

Использование результирующего dbml-файла

В результате работы обработки Elisy.LinqTo1CSql.81.epf появляется dbml-файл, который в Visual Studio выглядит так:

Внешний вид сгенерированной для Linq структуры базы данных 1С:Предприятие

Обработка создала dbml с определением пространства имен вида: Elisy.LinqTo1CSql.<имя конфигурации>. В дальнейшем обращение из C# можно выполнить так (здесь приведено определение класса контроллера из Asp.Net MVC):

using System.Data.Linq;
using System.Linq.Expressions;
using Elisy.LinqTo1CSql.УправлениеТорговлейДляКазахстана;
[HandleError]
public class ProductController : Controller
{
[OutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Item(string parentname, string names)
{
УправлениеТорговлейДляКазахстана dc = new УправлениеТорговлейДляКазахстана(ConfigurationManager.ConnectionStrings["GalTUS2ConnectionString"].ConnectionString);
Table<СправочникНоменклатура> table = dc.GetTable<СправочникНоменклатура>();
byte[] parentId = new Guid("00000000-0000-0000-0000-000000000000").ToByteArray();
var tableByParentIDRef = table.Where(item => item.Родитель == parentId);
...
}
}

Выводы

Представленный способ прямого доступа к данным 1С имеет свои преимущества по отношению к другим способам. Стоимость его невысока. Скорость доступа к данным соизмерима или выше других способов. Данные всегда остаются "свежими".

Недостатки также присутствуют. Необходимость регенерации dbml-файла при значимом изменении конфигурации. Ограниченное использование – невозможность использовать в файловом режиме работы 1С. Нужно быть предельно внимательными при изменении данных, так как ошибки могут привести к неработоспособной конфигурации 1С.

Elisy.LinqTo1CSql.81.Alpha.epf (15,88 kb)

Elisy.NetBridge.dll (129,00 kb)

Tags: , ,

1C.Net | Asp.Net | C#

Comments

Владимир
Владимир
10/20/2009 9:08:28 AM #

Насколько я понял, LinqToSql заточен исключительно под ASP.net
Это не имеет смысла, поскольку для ASP.net есть родной механизм доступа к данным а именно web расширение, которое предоставляет полный доступ к данным 1С штатными способами.

Elisy
Elisy
10/20/2009 12:58:31 PM #

LinkToSql заточен под .net framework - будь то Asp.Net или Windows-приложение.
Web-расширение работает через ком-соединение, а статья описывает прямой доступ к базе данных.

Elisy
Elisy
10/20/2009 12:59:18 PM #

Кому интересно - вот код сбора воедино номенклатуры, ЕИ, остатков и свойств номенклатуры:

var видыТоваров = from видТовара in dc.GetTable<РегистрСведенийЗначенияСвойствОбъектов>()
join видТовараСвойство in dc.GetTable<ПланВидовХарактеристикСвойстваОбъектов>() on видТовара.Свойство equals видТовараСвойство.Ссылка
join значениеСвойства in dc.GetTable<СправочникЗначенияСвойствОбъектов>() on видТовара.Значение equals значениеСвойства.Ссылка
where видТовараСвойство.Наименование == "Вид товара"
select new { НоменклатураСсылка = видТовара.Объект, ВидТовараСсылка = видТовара.Значение, ВидТовара = значениеСвойства.Наименование };

var производители = from производитель in dc.GetTable<РегистрСведенийЗначенияСвойствОбъектов>()
join производительСвойство in dc.GetTable<ПланВидовХарактеристикСвойстваОбъектов>() on производитель.Свойство equals производительСвойство.Ссылка
join значениеСвойства in dc.GetTable<СправочникЗначенияСвойствОбъектов>() on производитель.Значение equals значениеСвойства.Ссылка
where производительСвойство.Наименование == "Производитель"
select new { НоменклатураСсылка = производитель.Объект, ПроизводительСсылка = производитель.Значение, Производитель = значениеСвойства.Наименование };

var коллекции = from коллекция in dc.GetTable<РегистрСведенийЗначенияСвойствОбъектов>()
join коллекцияСвойство in dc.GetTable<ПланВидовХарактеристикСвойстваОбъектов>() on коллекция.Свойство equals коллекцияСвойство.Ссылка
join значениеСвойства in dc.GetTable<СправочникЗначенияСвойствОбъектов>() on коллекция.Значение equals значениеСвойства.Ссылка
where коллекцияСвойство.Наименование == "Коллекция"
select new { НоменклатураСсылка = коллекция.Объект, КоллекцияСсылка = коллекция.Значение, Коллекция = значениеСвойства.Наименование };

DateTime текущаяДата = DateTime.Now;
DateTime началоМесяца = new DateTime(текущаяДата.Year, текущаяДата.Month, 1);
var товарыНаСкладахИтоги = from товарНаСкладе in dc.GetTable<РегистрНакопленияТоварыНаСкладахИтоги>()
where (товарНаСкладе._Period == началоМесяца)
select new { Склад = товарНаСкладе.Склад, Номенклатура = товарНаСкладе.Номенклатура, Количество = товарНаСкладе.Количество };

var товарыНаСкладахДетали = from товарНаСкладе in dc.GetTable<РегистрНакопленияТоварыНаСкладах>()
let активность = 1 //товарНаСкладе.Активность.ToArray()[0]
where (началоМесяца <= товарНаСкладе.Период) && (товарНаСкладе.Период <= текущаяДата) && (активность == 1)
select new { Склад = товарНаСкладе.Склад, Номенклатура = товарНаСкладе.Номенклатура, Количество = товарНаСкладе._RecordKind == 0 ? товарНаСкладе.Количество : -1 * товарНаСкладе.Количество };

var товарыНаСкладах = from товарНаСкладе in товарыНаСкладахИтоги.Concat(товарыНаСкладахДетали)
group товарНаСкладе by new { товарНаСкладе.Номенклатура, товарНаСкладе.Склад } into группа
let количествоСумма = группа.Sum(элемент => элемент.Количество)
select new { Склад = группа.Key.Склад, Номенклатура = группа.Key.Номенклатура, Количество = количествоСумма };

var доступныеТовары = from товарНаСкладе in товарыНаСкладах
join номенклатура in dc.GetTable<СправочникНоменклатура>() on товарНаСкладе.Номенклатура equals номенклатура.Ссылка
join видТовара in видыТоваров on номенклатура.Ссылка equals видТовара.НоменклатураСсылка
join производитель in производители on номенклатура.Ссылка equals производитель.НоменклатураСсылка
join коллекция in коллекции on номенклатура.Ссылка equals коллекция.НоменклатураСсылка
join еи in dc.GetTable<СправочникЕдиницыИзмерения>() on номенклатура.ЕдиницаХраненияОстатков equals еи.Ссылка
where товарНаСкладе.Количество > 0
select new Товар
{
Ссылка = товарНаСкладе.Номенклатура,
Наименование = номенклатура.Наименование,
Остаток = (double)товарНаСкладе.Количество,
ВидТовара = видТовара.ВидТовара,
Производитель = производитель.Производитель,
Коллекция = коллекция.Коллекция,
ЕИ = еи.Наименование
};

Elisy
Elisy
10/20/2009 1:10:36 PM #

Еще, в Интернет ходят упорные разговоры, что прямой доступ к данным противоречит определенному пункту лицензии 1С. Вот что удалось найти в законах:
Данное требование лицензионного соглашения противоречит Статье 1334 п.1 "Исключительное право изготовителя базы данных" ГК РФ часть 4, а также Статье 25 п.1 и п.3 "Свободное воспроизведение программ для ЭВМ и баз данных. Декомпилирование программ для ЭВМ" Закона об авторском праве и смежных правах.
В Гражданском Кодексе сказано:
"Изготовителю базы данных, создание которой (включая обработку или представление соответствующих материалов) требует существенных финансовых, материальных, организационных или иных затрат, принадлежит исключительное право извлекать из базы данных материалы и осуществлять их последующее использование в любой форме и любым способом".
"Базой данных, создание которой требует существенных затрат, признается база данных, содержащая не менее десяти тысяч самостоятельных информационных элементов (материалов)."

Mia
Mia
10/23/2009 7:06:02 AM #

Elisy, спасибо за информацию.

Kuzma
Kuzma
10/23/2009 12:07:36 PM #

Все доступно и понятно! Спасибо!

Donya
Donya
10/28/2009 9:14:15 PM #

Автор продолжай в том же духе

им345
им345
11/2/2009 2:40:45 PM #

работаю программистом 1С
это то что не хватало часто при выгрузке больших массивов данных

Nardu
Nardu
11/7/2009 12:28:06 PM #

I was very pleased to find this site.I wanted to thank you for this great read!! I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you post.

fast payday loans
fast payday loans
12/10/2009 12:46:34 PM #

Yea nice Work !Laughing

mentalic
mentalic
12/22/2009 5:29:13 AM #

Алгоритм построения dbml файла-это ноу-хау?
Есть некие теоритические основы построения?
просто хотелось бы такую же технологию для других СУБД в частности для Oracle.

Elisy
Elisy
12/29/2009 7:30:00 AM #

DBML-файл генерируется в Visual Studio в cs-классы. Думаю, что Microsoft поддерживает только свою MS SQL таким образом. Конечно, есть возможность сделать генер самое для Oracle и использовать сторонние Linq Oracle компоненты, но интереса к таким инструментам не замечал. Связано это с тем, что Oracle поддерживается начиная с 1С 8.2, которая еще достаточно неосвоенная.

Powered by BlogEngine.NET 1.6.0.0
Original Design by Laptop Geek, Adapted by onesoft

Indonesia Furniture Handicraft Wholesale Marketplace : Bisnis Online Rahasia Dahsyat Hidup Sukses dan Uang Melimpah : Travelling : Salehdbrent Sgdashhousedotcom : Used Stationary Bikes : Salehdbrent Sgdashhousedotcom : Used Stationary Bikes : Used Stationary Bikes : Travel Jakarta Bandung: Blogger Indonesia Dukung Internet Aman, Sehat & Manfaat : Hosting Murah Indonesia Indositehost.com : Indonesia Furniture Handicraft Wholesale Marketplace