4 окт. 2013 г.

Разработка своего коннектора 1С для QlikView на C#

На сайте QlikCommunity можно найти материалы по SDK (v.11, v.10) для разработки своего коннектора. Нужные файлы находятся в разделе QVX SDK Libraries and Examples (QvxSDK_2.0.zip). В архиве находятся примеры, необходимые файлы для разработки, и документация. Для разработки нам потребуется Visual Studio. Знание C# необязательно, документации, примеров и умения пользоваться поиском оказалось вполне достаточно для создания примитивного коннектора.
На видео можно посмотреть, что у меня получилось, работа с QlikView начинается c 04:35.


Принцип работы коннектора

Создаем в Visual Studoi новый проект, копируем файлы QvxLibrary.dll и verpatch.exe из архива SDK в каталог проекта.


Добавляем ссылки на необходимые библиотеки в обозревателе решения.


На вкладке COM подключаем коннектор 1С.


На вкладке Обзор подключаем библиотеку QlikView


Выделяем проект C# (пункт connector1c), нажимаем Свойства (Alt+Enter).


Открываем вкладку "События построения", определяем событие после построения, в моем случае это "$(ProjectDir)verpatch.exe" "$(TargetPath)" /s "QlikView Connector" "start1c.blogspot.com"
Это нужно для того, чтобы после компиляции проекта запустился verpatch.exe и пропатчил созданный файл, иначе QlikView его не увидит. Наличие строки "QlikView Connector" обязательно, вторую строку определяете по своему усмотрению.


Создаем первый класс Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace connector1c
{

    static class Program
    {
        /// <summary>
        /// Главная точка входа для приложения.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            if (args != null && args.Length >= 2)
            {
                new Connector().Run(args[0], args[1]);
            }
        }
    }
}
 
Класс Connector.cs отвечает за создание строки подключения для скрипта загрузки.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QlikView.Qvx.QvxLibrary;
using System.Windows.Interop;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace connector1c
{
    internal class Connector : QvxServer
    {

        QvConnector qv_connector;

        public override QvxConnection CreateConnection()
        {
            qv_connector = new QvConnector();
            return qv_connector;
        }

        public override string CreateConnectionString()
        {
            var base_connector = CreateBaseConnector((qv_connector == null) ? null:qv_connector.MParameters);
            base_connector.ShowDialog();
            string connectionString = null;
            if (base_connector.DialogResult.Equals(true))
            {
                if (base_connector.GetServerBase())
                {
                    connectionString = String.Format("Srvr={0};Ref={1};UserId={2};Password={3};Query={4};QV_Table={5}",
                                       base_connector.GetBaseLocation(),
                                       base_connector.GetBaseName(),
                                       base_connector.GetUser(),
                                       base_connector.GetPassword(),
                                       base_connector.GetQueryText(),
                                       base_connector.getQV_Table());
                }
                else
                {
                    connectionString = String.Format("File={0};UserId={1};Password={2};Query={3};QV_Table={4}",
                                       base_connector.GetBaseLocation(),
                                       base_connector.GetUser(),
                                       base_connector.GetPassword(),
                                       base_connector.GetQueryText(),
                                       base_connector.getQV_Table());
                }
       
            }
            return connectionString;
        }

        private BaseConnector CreateBaseConnector(Dictionary<string, string> mParams = null)
        {
            var base_connector = new BaseConnector(mParams);
            var wih = new WindowInteropHelper(base_connector);
            wih.Owner = MParentWindow;
            return base_connector;
        }

    }
}
 
Класс QvConnector.cs реализует подключение к 1С по COM соединению, передачу текста запроса и считывание результата.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Authentication;
using System.Text.RegularExpressions;
using QlikView.Qvx.QvxLibrary;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;

namespace connector1c
{
    [ComVisible(true)]
    class QvConnector : QvxConnection
    {

        string connString;
        V82.COMConnector v8connector;
        string Srvr, File, Ref, Usr, Pwd, Query, QV_TableName;

        public override void Init()
        {

            MParameters.TryGetValue("File", out File);
            MParameters.TryGetValue("Srvr", out Srvr);
            MParameters.TryGetValue("Ref", out Ref);
            MParameters.TryGetValue("UserId", out Usr);
            MParameters.TryGetValue("Password", out Pwd);
            MParameters.TryGetValue("Query", out Query);
            MParameters.TryGetValue("QV_Table", out QV_TableName);

            if (Srvr == null)
            {
                connString = "File='" + File + "';Usr='" + Usr + "';Pwd='" + Pwd + "';";
            }
            else
            {
                connString = "Srvr='" + Srvr + "';Ref='" + Ref + "';Usr='" + Usr + "';Pwd='" + Pwd + "';";
            }

            dynamic v8com = GetV8Connect();
            if (v8com == null) return;

            dynamic v8qBuilder = v8com.NewObject("QueryBuilder");
            v8qBuilder.Text = Query;
            var v8Fileds = new QvxField[v8qBuilder.Result.Columns.Count];
            int i = 0;
            foreach (var v8Column in v8qBuilder.Result.Columns)
            {
                v8Fileds.SetValue(new QvxField(v8Column.name, QvxFieldType.QVX_TEXT, QvxNullRepresentation.QVX_NULL_FLAG_SUPPRESS_DATA, FieldAttrType.ASCII), i++);
            }

            MTables = new List<QvxTable> {
                new QvxTable {
                    TableName = QV_TableName,
                    GetRows = GetData,
                    Fields = v8Fileds
                }
            };

            Marshal.ReleaseComObject(v8qBuilder);
            Marshal.ReleaseComObject(v8com);
            Marshal.ReleaseComObject(v8connector);
   
        }

        private dynamic GetV8Connect()
        {
            v8connector = new V82.COMConnector();
            return (v8connector == null) ? null : v8connector.Connect(connString);
        }

        private IEnumerable<QvxDataRow> GetData()
        {
            QvxTable table = FindTable(QV_TableName, MTables);

            if (table == null) yield return null;

            dynamic v8com = GetV8Connect();
            if (v8com == null)
            {
                yield return new QvxDataRow();
            }

            dynamic v8Query = v8com.NewObject("Query");
            v8Query.Текст = Query;
            dynamic result = v8Query.Execute;
            dynamic m = result.Choose;
            dynamic rezColumns = result.Columns;
            var columnIdx = new Dictionary<string, int>();
            foreach (var mx in rezColumns)
            {
                columnIdx.Add(mx.name, rezColumns.IndexOf(mx));
            }

            QvxDataRow row;
            while (m.Next() == true)
            {
                row = new QvxDataRow();
                foreach(var tField in table.Fields)
                {
                    int idx;
                    if (columnIdx.TryGetValue(tField.FieldName, out idx))
                    {
                        row[tField] = v8com.String(m.Get(idx));
                    };
                }
                yield return row;
            }

            Marshal.ReleaseComObject(v8Query);
            Marshal.ReleaseComObject(result);
            Marshal.ReleaseComObject(m);
            Marshal.ReleaseComObject(v8com);
            Marshal.ReleaseComObject(v8connector);

        }

        public override QvxDataTable ExtractQuery(string query, List<QvxTable> qvxTables)
        {
            return base.ExtractQuery(query, qvxTables);
        }

    }
}
 
Для реализации ввода параметров подключения создана форма BaseConnector.xaml.
<Window x:Class="connector1c.BaseConnector"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            mc:Ignorable="d"
            Title="Параметры подключения" WindowStartupLocation="CenterOwner" Width="430" Height="438" ResizeMode="CanResize" ShowInTaskbar="False">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="373*"/>
            <RowDefinition Height="35"/>
        </Grid.RowDefinitions>

        <CheckBox x:Name="ServerBase" Content="Клиент/Сервер" HorizontalAlignment="Left" Margin="122,9,0,0" VerticalAlignment="Top" Width="290" Click="ServerBase_Click_1" Height="16"/>

        <TextBox x:Name="base_location" Height="23" Margin="122,30,10,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
        <TextBox x:Name="base_name" Height="23" Margin="122,58,10,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
        <Label x:Name="label_location" Content="Путь" HorizontalAlignment="Left" Margin="10,27,0,0" VerticalAlignment="Top" Width="87" Height="26"/>
        <Label x:Name="label_name" Content="Имя базы" HorizontalAlignment="Left" Margin="10,56,0,0" VerticalAlignment="Top" Width="87" Height="26"/>

        <Label x:Name="username_label" Content="Пользователь" HorizontalAlignment="Left" Margin="10,93,0,0" VerticalAlignment="Top" Width="87" Height="26"/>
        <Label x:Name="password_label" Content="Пароль" HorizontalAlignment="Left" Margin="10,121,0,0" VerticalAlignment="Top" Width="87" Height="26"/>
        <TextBox x:Name="username" Height="23" Margin="122,93,10,0" TextWrapping="Wrap" VerticalAlignment="Top" ToolTip="Login"/>
        <PasswordBox x:Name="password" Height="23" Margin="122,121,10,0" VerticalAlignment="Top" ToolTip="Password"/>

        <TextBox x:Name="query_text" KeyboardNavigation.AcceptsReturn="True" Margin="10,219,10,10"  TextWrapping="Wrap" UndoLimit="93" ToolTip="Текст запроса"/>

        <Button x:Name="btnOk" Content="Ok" HorizontalAlignment="Left" Margin="10,0,0,10" Width="75" Click="btnOk_Click" Grid.Row="1"  />
        <Button x:Name="btnCancel" Content="Cancel" HorizontalAlignment="Left" Margin="90,0,0,10" Width="75" Click="btnCancel_Click" Grid.Row="1"/>
        <Label Content="Текст запроса" HorizontalAlignment="Left" Margin="10,188,0,0" VerticalAlignment="Top" Width="87" Height="26"/>
        <Label x:Name="QV_table_label" Content="Таблица QV" HorizontalAlignment="Left" Margin="10,152,0,0" VerticalAlignment="Top" Width="87" Height="26"/>
        <TextBox x:Name="qv_table" Height="23" Margin="122,155,10,0" TextWrapping="Wrap" VerticalAlignment="Top" ToolTip="Таблица QlikView"/>
        <Label Margin="0,0,10,10" FontSize="9" Grid.Row="1" HorizontalAlignment="Right" Width="98" ToolTip="Welcome :)" >
            <Hyperlink NavigateUri="http://start1c.blogspot.com" RequestNavigate="Hyperlink_Start1c" Block.TextAlignment="Right" >start1c.blogspot.com</Hyperlink>

        </Label>
    </Grid>
</Window>
 
Логика взаимодействия с формой описана в классе BaseConnector.xaml.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Controls;
using System.Net.NetworkInformation;
using System.Diagnostics;

namespace connector1c
{
    /// <summary>
    /// Логика взаимодействия для BaseConnect.xaml
    /// </summary>
    public partial class BaseConnector : Window
    {
        public BaseConnector(Dictionary<string, string> mParams = null)
        {
            InitializeComponent();

            if (mParams != null)
            {
                string Srvr, File, Ref, Usr, Pwd, Query, QV_TableName;
                mParams.TryGetValue("File", out File);
                mParams.TryGetValue("Srvr", out Srvr);
                mParams.TryGetValue("Ref", out Ref);
                mParams.TryGetValue("UserId", out Usr);
                mParams.TryGetValue("Password", out Pwd);
                mParams.TryGetValue("Query", out Query);
                mParams.TryGetValue("QV_Table", out QV_TableName);

                if (Srvr == null)
                {
                    ServerBase.IsChecked = false;
                    base_location.Text = File;
                }
                else
                {
                    ServerBase.IsChecked = true;
                    base_location.Text = Srvr;
                    base_name.Text = Ref;
                }
                username.Text = Usr;
                password.Password = Pwd;
                query_text.Text = Query;
                qv_table.Text = QV_TableName;
            }
            setVisibility();

        }

        private void setVisibility()
        {
            Visibility elemVisibility = (ServerBase.IsChecked.Value) ? Visibility.Visible : Visibility.Hidden;
            label_name.Visibility = elemVisibility;
            base_name.Visibility = elemVisibility;
            label_location.Content = (ServerBase.IsChecked.Value) ? "Сервер" : "Каталог";
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = false;
            Close();
        }

        private void btnOk_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = true;
            Close();
        }

        private void ServerBase_Click_1(object sender, RoutedEventArgs e)
        {
            setVisibility();
        }

        public bool GetServerBase()
        {
            return ServerBase.IsChecked.Value;
        }

        public string GetBaseLocation()
        {
            return base_location.Text;
        }

        public string GetBaseName()
        {
            return base_name.Text;
        }

        public string getQV_Table()
        {
            return qv_table.Text;
        }

        public string GetQueryText()
        {
            return query_text.Text;
        }

        public string GetUser()
        {
            return username.Text;
        }

        public string GetPassword()
        {
            return password.Password;
        }

        private void Hyperlink_Start1c(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
        {
            Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
            e.Handled = true;
        }

    }
}
 
После компиляции проекта полученный файл и библиотеку QvxLibrary.dll необходимо скопировать в %PROGRAMFILES%\Common Files\QlikTech\Custom Data\<connector dir>, в моем случае это C:\Program Files (x86)\Common Files\QlikTech\Custom Data\connector1c. Отсутствующие каталоги необходимо создать вручную. После этого запускаем QlikView, коннектор доступен в списке вариантов подключения.



Исходники проекта C#, connector1c.zip. Успехов в создании своих коннекторов.

Update 09/12/2015
Коллега наткнулся на проблему при использовании verpatch из архива SDK. После сборки проекта при патче результата возникала ошибка:

Exception in ParseBinaryVersionResource
Error in ParseBinaryVersionResource
error parsing version info from the file
Some of actions failed, exiting

Для решения проблемы необходимо скачать обновленную версию verpatch. В документации SDK есть ссылка на ресурс http://www.codeproject.com/KB/install/VerPatch.aspx. Так же можно использовать альтернативный источник https://ddverpatch.codeplex.com.

63 комментария:

  1. Михаил, добрый день.
    При попытке запустить ваш коннектор на Windows Server 2008 R2
    (брал файлы из папки connector1c.zip\connector1c\connector1c\bin\Release)
    Программа коннектор видит, но при попытке соединиться выдаёт ошибку:
    "Выбранный соединитель прекратил работу или был отключен
    connector1c.exe (32)(start1c.blogspot.com)"
    Что можно сделать в данной ситуации?
    Заранее, спасибо.
    С уважением, Антон.

    ОтветитьУдалить
    Ответы
    1. День добрый,

      для начала нужно проверить, установлена ли платформа 1С на этом сервере. Коннектор использует библиотеку comcntr.dll для подключения.

      Удалить
  2. День добрый Михаил,
    не подскажите с чем может быть связана данная ошибка:
    QVX_UNKNOWN_ERROR: Не удалось получить фабрику класса COM для компонента с CLSID {2B0C1632-A199-4350-AA2D-2AEE3D2D573A} из-за следующей ошибки: 80040154 Класс не зарегистрирован (Исключение из HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))

    ОтветитьУдалить
  3. Полагаю, разработка ведется на x64 системе. COM-connector 1С является 32-битным.
    Посмотрите
    http://forum.infostart.ru/forum14/topic33442/
    http://devtrainingforum.v8.1c.ru/forum/thread.jsp?id=558085&threadtype=0
    http://miko.ru/blogs/SwordBlog/54/
    http://biconsult.ru/sites/default/files/downloads/BIConsult_QlikView-1C-Connector_11.3_User_guide.pdf

    Я на своей x64 машине регистрировал COM-connector в COM+ компонентах.
    http://www.sql.ru/forum/802087/v81-comconnector-na-server-2008-r2-64
    http://kb.mista.ru/article.php?id=870

    ОтветитьУдалить
    Ответы
    1. Спасибо Михаил за ответ, вроде все сделал, но теперь появилась другая ошибка:

      System.NullReferenceException: Ссылка на объект не указывает на экземпляр объекта.
      в System.Dynamic.ExcepInfo.GetException()
      в System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
      в CallSite.Target(Closure , CallSite , ComObject )
      в System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
      в CallSite.Target(Closure , CallSite , Object )
      в System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
      в connector1c.QvConnector.Init() в d:\!tmp\qw\connector1c\connector1c\QvConnector.cs:строка 46
      в QlikView.Qvx.QvxLibrary.QvxConnection.Connect(String connectionString, QvxConnectOptions options, QvxServer qvxServer)
      в QlikView.Qvx.QvxLibrary.QvxServer.HandleConnect(QvxRequest request)
      в QlikView.Qvx.QvxLibrary.QvxServer.HandleRequest(QvxRequest request)
      в QlikView.Qvx.QvxLibrary.QvxServer.Run(String parentString, String pipeName)
      У меня Win7 x64, Qv32...
      Где может еще быть мой бок?
      Спасибо, очень полезная у Вас инфа!!!

      Удалить
    2. Могу предположить, что это из-за перекомпиляции на основе моего проекта. На всякий случай стоит заново подключить библиотеку QvxLibrary к текущему проекту.

      Удалить
    3. Добрый день, Михаил.
      Я зарегистрировал COM-connector в COM+ компонентах (по инструкции http://kb.mista.ru/article.php?id=870)
      И назвал его connector1c
      Загрузка по прежнему выдаёт ошибку: Не удалось получить фабрику класса COM для компонента с CLSID {2B0C1632-A199-4350-AA2D-2AEE3D2D573A} из-за следующей ошибки: 80040154 Класс не зарегистрирован (Исключение из HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).. Stack trace written to C:\ProgramData\QlikTech\Custom Data\connector1c\Log\StackTrace.txt

      Что необходимо подправить в коде?
      Могли бы вы выложить перекомпилированный коннектор, для подключения к 64 битному серверу?

      Удалить
    4. День добрый, в архиве лежат две версии, для x86 и для x64.
      В коде больше поправить нечего, на всякий случай проверьте активность ссылки на com connector 1С в свойствах проекта.

      Удалить
    5. Андрей Глухов15 июня 2016 г., 14:31

      Добрый день, Михаил!
      Вы можете выложить версию для 1С 8.3
      Заранее благодарен!

      Удалить
    6. Андрей, доброго времени суток.
      Выкладывать не буду, скачать и перекомпилировать вы сможете сами, информации достаточно.
      Более того, советую вам не заморачиваться с коннекторами, работающими с 1С через COM-соединение.
      Это самый медленный вариант реализации получения данных, не считая нагрузки на базу при больших объемах запрашиваемых данных. Вообще, любой коннектор, обращающийся к базе 1С можно считать нежелательным решением. На мой взгляд, лучше реализовать консолидацию необходимых данных во внешнюю СУБД, тот же самый SQL, и дергать данные из него.

      Удалить
  4. Юлия Дошлов27 марта 2014 г., 14:06

    Михаил, Добрый день!
    Скомпилировала коннектор для работы с 1С 8.3. Вроде всё корректно отрабатывает, в логи записи никакие не уходят, ошибок не выдает. Но окно с результатом загрузки в QlikView пустое. Данные не загружаются.
    Подскажите пожалуйста, с чем это может быть связано?

    ОтветитьУдалить
    Ответы
    1. День добрый. А доступные поля при создании новых объектов qlikview появляются?

      Удалить
    2. Юлия Дошлова21 апреля 2014 г., 14:32

      Спасибо большое, всё решились припиской вот такой вот части кода (SQL Select * From продажи;) после текста, сформированного коннектором. Данные загрузились как следует. Спасибо Вам за Ваше замечательное творение!

      Удалить
    3. Вам спасибо, что воспользовались :) Понял, в чем ваша проблема была. Если посмотрите на видео, на время 5:38 - изначально текст генерируется без команды SQL SELECT ... После нажатия на кнопку "Выбрать" появляется конструктор выражения SELECT (5:50). После конструирования выражения финальная часть кода (SQL Select * FROM Продажи;) появляется скрипте загрузки. После этого можно выполнять скрипт.

      Удалить
  5. Анонимный17 июля 2014 г., 13:21

    Доброго времени суток, Михаил.
    Подскажите пожалуйста, когда вы регистрировали COM-connector, какое Вы давали ей имя?

    ОтветитьУдалить
  6. comconnector создал вот такая ошибка стала вываливаться, не могу разобраться в чём дело. помогите
    QVX_UNKNOWN_ERROR: В результате вызова компонента COM возвращена ошибка в формате HRESULT E_FAIL.. Stack trace written to C:\Documents and Settings\All Users\Application Data\QlikTech\Custom Data\connector1c\Log\StackTrace.txt

    ОтветитьУдалить
    Ответы
    1. День добрый. Полагаю, у вас что-то с регистрацией com-connector'а 1С. Выше по ссылкам можно поискать решение. У меня такой проблемы не возникало, вряд ли что-то подскажу.

      Удалить
  7. Анонимный3 мая 2015 г., 22:12

    Добрый день, воспользовался Вашим коннектором для загрузки данных из 1с. Данные загрузил, но могу только сформировать простую таблицу, диаграммы создать не получается. Что не так сделал?

    ОтветитьУдалить
    Ответы
    1. Привет. Если простая таблица формируется, то и диаграмма должна тоже без проблем создаваться. Можно попробовать построить на этих данных простую гистограмму, с одним измерением и выражением. Если не получиться, то тут только смотреть надо.

      Удалить
    2. Анонимный4 мая 2015 г., 12:00

      Уже пробовал, пишет нет данных для отображения. Выгружал данные из 1с в таблицу excel, потом в qlik подгружал все нормально формирует. Что смотреть не подскажите?

      Удалить
    3. Думаю, понял. Скорей всего проблема с типом поля, qlik не определяет его как числовое. Проверьте в вашей таблице (Ctrl+Alt+D, вкладка "Таблицы") какие теги у полей, которые должны считаться в диаграмме.

      Удалить
    4. Вот и ответ, должен быть так же тэг numeric или integer. Смотрите тогда сам запрос, по нужному полю где то возвращается значение, которое qlik не может распознать как числовое. Либо кидайте текст запроса сюда.

      Удалить
    5. Анонимный5 мая 2015 г., 15:49

      запрос из 1с?

      Удалить
    6. Анонимный5 мая 2015 г., 16:03

      ВЫБРАТЬ
      ПродажиОбороты.Номенклатура,
      ПродажиОбороты.ДоговорКонтрагента,
      ПродажиОбороты.Организация КАК Организация,
      ПродажиОбороты.Контрагент,
      СУММА(ПродажиОбороты.КоличествоОборот) КАК КоличествоОборот,
      СУММА(ПродажиОбороты.СтоимостьОборот) КАК СтоимостьОборот
      {ВЫБРАТЬ
      Организация.*,
      Контрагент.*,
      ДоговорКонтрагента.*,
      Номенклатура.*}
      ИЗ
      РегистрНакопления.Продажи.Обороты КАК ПродажиОбороты
      ГДЕ
      НЕ ПродажиОбороты.СтоимостьОборот = 0

      СГРУППИРОВАТЬ ПО
      ПродажиОбороты.Номенклатура,
      ПродажиОбороты.ДоговорКонтрагента,
      ПродажиОбороты.Организация,
      ПродажиОбороты.Контрагент

      УПОРЯДОЧИТЬ ПО
      Организация

      Удалить
    7. Да, можно в почту, ссылка вверху страницы.

      Удалить
    8. Анонимный6 мая 2015 г., 17:27

      запрос здесь оставил.

      Удалить
    9. Запрос в порядке, я на таких же коннектор тестировал. Попробуйте, если есть возможность, ради эксперимента убрать из текста запроса конструкцию в фигурных скобках. Коннектор использует построитель запроса 1С для получения колонок, может где-то и врет с типами. Сам смогу попробовать только после праздников :)

      Удалить
  8. Добрый день, Михаил! а можно подобный коннектор создать для 1с 7.7? И в чем будут отличия?

    ОтветитьУдалить
    Ответы
    1. День добрый, Дмитрий. Да, в общих чертах, можно. В классе QvConnector необходимо переписать с учетом языка 77 участок получения заголовков полей колонок запроса. Для v8 я получал с помощью построителя запроса:
      dynamic v8qBuilder = v8com.NewObject("QueryBuilder");

      Так же необходимо переписать участок загрузки результата запроса, начинается с
      dynamic v8Query = v8com.NewObject("Query");

      Удалить
    2. Да, да, я нашел эти участки кода. Я буду использовать выгрузку результата запроса в ТаблицуЗначений и получать заголовки столбцов оттуда. Сложность в в том что работа с объектом V77.Application проходит с помощью функций InvokeMember, а это очень неудобно. Посоветуйте, что лучше: написать простенькую обертку для этого объекта или не мучиться с ней?

      Удалить
    3. Посмотрел примеры с 7.7, InvokeMember вроде бы и одной строкой, а большинство параметров неизменно. Я бы вынес в отдельную функцию, встреться мне такой участок в коде более двух раз :)

      Удалить
    4. да, так будет лучше всего, понятнее по крайней мере)) Спасибо за ваш ответ!

      Удалить
    5. Добрый день, Михаил! появилась одна проблема с коннектором к 1с7.7: если оставить методы освобождения ресурсов Marshal.ReleaseComObject(...);... в методе public override void Init() класса QvConnector, то появляется ошибка: типа "не могу найти СОМ объект". А если эти методы убрать, то в памяти остается висеть 1ска...
      Не могу понять куда мне разместить методы освобождения СОМ объектов

      Удалить
    6. а, все сам разобрался)) Создал обертку для СОМ объекта и вызывал Marshal.ReleaseComObject(...) для нее, а не для самого объекта)))

      Удалить
    7. теперь другая проблема: запрос к базе выполняется два раза - 1 - для получения заголовков, 2 - для получения строк. Что с этим делать не знаю, в 1сV7.7 кажется нет QueryBuilder

      Удалить
    8. Думаю, стоит добавить параметр в процедуру получения данных. Ориентируясь на значение параметра можно, например, при получении заголовков программно добавлять в запрос невыполнимое условие. Или же условие добавить в текст запроса по умолчанию, потом удалять по условию. Либо ввести в запрос переменную. Под рукой нет базы с реальными данными, чтобы проверить быстродействие, набросал для примера:

      Запрос = СоздатьОбъект("Запрос");
      ТекстЗапроса =
      "//{{ЗАПРОС(Сформировать)
      |ТекущийЭлемент = Справочник.Спр.ТекущийЭлемент;
      |Код = Справочник.Спр.Код;
      |Рек1 = Справочник.Спр.Рек1;
      |Рек2 = Справочник.Спр.Рек2;
      |Группировка ТекущийЭлемент;
      |Условие(ВсеСтроки = 1);
      |"//}}ЗАПРОС
      ;
      Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
      Возврат;
      КонецЕсли;
      Т = СоздатьОбъект("ТаблицаЗначений");
      Запрос.Выгрузить(Т, 1);

      Удалить
    9. Этот комментарий был удален автором.

      Удалить
    10. Спасибо, проверю. Еще одна неожиданность от QlikView - при формировании строки подключения в скрипт заносится в том числе и текст запроса, так вот QlikView ругается на элементы запроса типа: "Слово" пробел "Слово", например:
      Группировка ТекущийЭлемент.
      Можно ли ему объяснить чтобы он "не умничал" и не контролировал эту строку?

      Удалить
    11. Скиньте, что получается в строке подключения.

      Удалить
    12. CUSTOM CONNECT TO "Provider=connector1c.exe;File=C:\\1C\\DB\;Query=Наименование = Справочник.Номенклатура.Наименование;Код = Справочник.Номенклатура.Код;Группировка Наименование;Группировка Код;Без Итогов;;QV_Table=Таблица;XUserId=GDFGBIDSQGNBNeCaYFUbLIDXROADNBGaAEUBbILQQWPDNAKI;XPassword=MFJDaYD;";

      Удалить
    13. Вроде нормальная строка. Можно еще текст ошибки самого QlikView? А ругается только на группировки? или на "Без итогов" тоже?
      Проверьте, что получится, если удалить эти строки:
      Группировка Наименование;
      Группировка Код;
      Без Итогов;

      Удалить
    14. QVX_UNKNOWN_ERROR: Parameter "Группировка Наименование" has no value, or misplaced semicolon

      Удалить
    15. Кажется ему не нравились точки-с-запятой (;)

      Удалить
    16. CUSTOM CONNECT TO "Provider=connector1c.exe;File=C:\\1C\\MILEX\;Query=Наименование = Справочник.Номенклатура.Наименование
      Код = Справочник.Номенклатура.Код
      Группировка Наименование
      Группировка Код
      Без Итогов;QV_Table=Номеклатура;XUserId=JDBKEILSQGMBddCaVFUbbILQQGOBdeGT;XPassword=AFWEAdD;";

      Вот эта строка всех устроила))

      Удалить
    17. Этот комментарий был удален автором.

      Удалить
    18. всех кроме 1С (((
      Придется что-то в коде кодить чтоб запятые вставить в текст запроса...

      Удалить
    19. CUSTOM CONNECT TO "..." по сути, является строкой с разделителями в виде ";". Описание каждой секции состоит из переменной, "=" и значения переменной. В запросе для v8 все просто, там символы ";" отсутствуют. Для запроса v7 у нас соответственно проблема.

      Удалить
    20. решил их следующим образом:
      в сроке запроса вместо ";" ставлю "|",

      Наименование = Справочник.Номенклатура.Наименование
      |Код = Справочник.Номенклатура.Код
      |Группировка Наименование
      |Группировка Код
      |Без Итогов|

      в коде выполняю Replace('|', ';'), и месте получения заголовков полей к строке запроса добавляю Условие(0 = 1);

      Все вроде работает и лишний раз запрос не выполняется.

      Вот еще какой вопрос, можно ли в коннекторе реализовать полосу прогресса на время выполнения запроса?

      Удалить
    21. Поздравляю :) По поводу индикатора выполнения - настолько глубоко не копал. Лучше обратиться к документации, ссылки в начала страницы.

      Удалить
    22. Добрый день, Михаил! Скажите существуют соответствующие библиотеки для разработки коннектора к Qlik Sense?

      Удалить
    23. Я не интересовался. Для Sense существует отдельный раздел https://community.qlik.com/community/qlik-sense/resource-library, можете посмотреть там.

      Удалить
    24. Собственно :) https://help.qlik.com/sense/en-US/developer/

      Удалить
    25. Ага, я нашел эту ссылку. Буду разбираться. На первый взгляд проблем быть не должно.

      У меня еще один вопрос появился . важный, но, в принципе, не критичный: Если передать коннектору запрос с ошибкой, то 1С виснет, приходится снимать задачу, а потом индексировать базу... Можно ли как-то перехватить эту исключительную ситуацию и предотвратить зависание 1С?
      В коннекторе вставляю вызов запроса в блок try-catch, но это не спасает, коннектор не ругается, а 1С все равно висит.
      ExecuteBatch() выолняет последовательность операторов,но не возвращает значение, а EvalExpr() возвращает результат, но не выполняет последовательность операторов... заколдованный круг, блин...

      Удалить
    26. Вряд ли подскажу. В качестве альтернативы могу предложить создать экспортную функцию в общем модуле, передавать в нее текст запроса, выполнять в обертке Попытка Исключение КонецПопытки, возвращать таблицу значений, либо 0 в случае ошибки.

      Удалить
    27. Добрый день, Михаил! Продолжаю терроризировать вас вопросами)))
      Вопрос в следующем: после передачи данных из 1с с помощью коннектора они распознаются QlikView как строковый тип, а не как дата или число(( Что можно с этим поделать?

      Удалить
    28. Добрый. У меня за это это в процедуре IEnumerable, код:
      row = new QvxDataRow();
      foreach(var tField in table.Fields)
      {
      int idx;
      if (columnIdx.TryGetValue(tField.FieldName, out idx))
      {
      row[tField] = v8com.String(m.Get(idx));
      };
      }
      Как видите, я обращался к функции 1С для преобразования значения в строку.
      Мне было этого достаточно, полученные строковые значения QlikView корректно распознавал в необходимые типы.
      Вы можете добавить в этот участок дополнительные проверки на типы 1С и соответсвующие обработки значений.

      Удалить
    29. ...QlikView корректно распознавал в необходимые типы....
      т.е. числа видел как числа, а не как строки? Или вам и нужны были только строки?

      Удалить
    30. Вот еще одна строка:
      v8Fileds.SetValue(new QvxField(v8Column.name, QvxFieldType.QVX_TEXT, QvxNullRepresentation.QVX_NULL_FLAG_SUPPRESS_DATA, FieldAttrType.ASCII), i++);
      и параметр FieldAttrType.ASCII говорит о том как QlikView интерпретировать тип поля. Тут стоит строковый тип

      Удалить
    31. ... FieldAttrType.INTEGER - целое и т.д., сложность в том как определить тип значения поля в ТаблицеЗначений 1с

      Удалить
  9. Этот комментарий был удален автором.

    ОтветитьУдалить