понедельник, 16 декабря 2013 г.

Error: Unknown Return Type, The return types for the following stored procedures could not be detected

During our last project we decided to use Linq to SQL context for object mapping as we needed to use only few stored procedures from MSSQL database.
But when I tried to map required procedures I'd got an error: Error: Unknown Return Type, The return types for the following stored procedures could not be detected. 
At glance research I found following articles according the issue: Unknow return type errorHow to get multiple result set of procedure using LINQ to SQL.
I knew exactly that there weren't declared any temporary tables in these stored procedures. But I wasn't quite sure about multiply results. In fact, it were very big stored procedures, about 2000 rows in each,  moreover I didn't write the SPs by myself, so it was T-SQL encapsulation in action :). As a result of this I decided to find out how Linq to SQL gets metadata information about SP result fields. Via Mr. Google I found following: Getting Return Metadata from Stored Procedures
I tried it and you can see what I've got:

But it wasn't last problem, during the query's execution I got this nice message:
After a few seconds I found out that the problem comes from external CLR library that our database  actually use. As a matter of fact there was a following scenario: when the Linq to SQL tried to get stored procedure metadata it called all sp's inner  functions and procedures and as you can see from the picture above when FMTONLY mode set some arguments to the CLR function "RegexGetMatchGroupValue" it returns exception.
When I fixed multiply results set and this error in CLR everything had started to work perfectly.
So the main idea is: if you have got error when you're adding stored procedure to Linq to Sql data context. Firstly, try it with FMTONLY mode. It will show you all errors that can be reason of the headache.

четверг, 18 апреля 2013 г.

Visual Studio project AfterBuilt event

Если вдруг вы используете два .exe приложения в решении Visual Studio где один проект ссылается на другой (т.е. использует его открытые классы) то может возникнуть проблема с компиляцией конфигурационного файла того проекта на который вы ссылаетесь.
Например:
Есть проект Master, расположен в С:\MasterAndSlave\Master
Есть проект Slave, расположен в C:\MasterAndSlave\Slave

Оба они расположены в одном решении MasterAndSlave при этом Master ссылается на Slave

У проекта Slave есть конфигурационный файл App.config который содержит нужные Slave параметры для работы. 
При компиляции проекта Master в его bin\[Имя конфигурации] директории мы получим список файлов:
  • Master.exe - исполняемый файл основного проекта
  • Master.vshost.exe - файл для работы в режиме Debug (если не выбрана конфигурация Release)
  • Master.exe.config - конфигурационный файл Master-а
  • Slave.exe - исполняемый файл Slave
  • Slave.exe.config - конфигурационный файл Slave-а
На первый взгляд все нормально, но если по каким либо причинам вам понадобиться изменить App.config в Slave проекте, то возникнут проблемы. А именно скомпилированный для Master файл Slave.exe.config останется без каких либо изменений, что может конкретно осложнить вам жизненный путь к успеху :).

Решается проблема не очень сложно, нужно добавить к событию AfterBuild копирование нужно нам файла конфига.
Отрываете файл проекта Master.csproj/vbproj в любом текстовом редакторе и добавляете следующие изменения:

<Target Name="AfterBuild">
    <Copy SourceFiles="$(ProjectDir)\[Slave_Folder]\bin\$(Configuration)\[Slave_exe_file_name].exe.config" 
          DestinationFolder="$(TargetDir)" />
  </Target>

Что для вышеописанного примера выглядит так:

<Target Name="AfterBuild">
    <Copy SourceFiles="$(ProjectDir)\Slave\bin\$(Configuration)\Slave.exe.config" 
          DestinationFolder="$(TargetDir)" />
  </Target>

вторник, 9 октября 2012 г.

DevExpress TreeList и его скролбары или как запретить программно изменять прокрутку

Я не знаю у кого как, а у меня с этим делом вылезли проблемы. В одном проекте требовалось сделать так чтобы нельзя было программно крутить вертикальный ScrollBar в TreeList-е. Требовалось потому, что при изменении свойства CurrentNode TreeList почему то начинал сам себя вертеть (добавлю что разобраться почему он так делает мне досконально не удалось ибо нужно было дизасэмблить библиотеку DevExpress да и создавать дочерний TreeList класс чтобы изменить это, что мне собственно очень не хотелось). Фигня, подумал я  и замутил событие MouseWheel от горячо любимого TreeList-а. Апосля, подумав, добавил TopVisibleNodeIndexChanged для контроля индекса ScrollBar-а.
Выглядело вот как то так:
//отлов пользовательского и программного сролинга при прокрутке колеса мыши
        private bool wheeled = false;
        private int index =0;
void fList_MouseWheel(object sender, MouseEventArgs e)
        {
            wheeled = true;
        }

        void fList_TopVisibleNodeIndexChanged(object sender, EventArgs e)
        {
            if (!wheeled )
            {
                if (fList.TopVisibleNodeIndex != index)
                    fList.TopVisibleNodeIndex = index;
            }
            wheeled = false;
        }

void fList_MouseDown(object sender, MouseEventArgs e)
        {
            index = fList.TopVisibleNodeIndex;
        }
Тут как все уже наверно догадались   fList - это объект класса DevExpress.XtraTreeList.TreeList и все описанные выше методы являются событиями от него. Поле wheeled  - говорит о том крутиться ли Scroll мышью или кто "другой"(один из разработчиков DevExpress?) внезапно захотел это сделать, а поле index запоминает позицию верхнего видимого узла TreeList, что и ясно из названия самого свойства TopVisibleNodeIndex (Ваш К.О.).
Я сначала было подумал что все уже готово, колесом прокручивается, при селекте узла прокрутки нет, но при экспресс тестировании выявилось, что колесом то прокручивается, а вот  мышью прокручивать нажатием на сам ScrollBar нельзя, и вот тут то и понеслась.

Поначалу я хотел использовать тот же MouseDown,  ан нет - почему то он при нажатии на ScrollBar не обрабатывается. Да и вообще, как оказалось, public свойства отображающее контрол ScrollBara  нет, есть только несколько булевых свойств для управлением видимости вертикального и горизонтального ScrollBara  и собственно событие MouseWheel, а так же свойство TopVisibleNodeIndex.
В общем, на то чтобы узнать как это можно сделать, у меня ушло немало времени для такой простейшей задачи, в конце я уже все таки начал гладить взглядом рефлектор и внезапно гугл выдал невиданную вещь. Оказывается таки есть там свойство со ScrollBar-ом и зовут его ScrollInfo. Проблема изначально была в том что оно private и фиг ты его найдешь. В общем остальное оказалось делом техники. Данное свойство содержит объект класса DevExpress.XtraTreeList.Scrolling.ScrollInfo. Собственно при просмотре документации именно он и навел меня на подозрительные мысли по поводу того что там как бы все таки что то да быть должно.


using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Scrolling;

class DynamicScrollBlocker
    {
         //отлов пользовательского и программного сролинга при прокрутке колеса мыши
        private bool wheeled = false;
        private int index =0;
        private bool scrollBarClicked = false;
        private TreeList fList;

        public DynamicScrollBlocker(TreeList listTree)
        {
               if(listTree==null) throw new Exception("WTF???!!!11");
               fList = listTree;
//находим таки треклятое свойство
               var pi = typeof(DevExpress.XtraTreeList.TreeList).GetProperty("ScrollInfo", BindingFlags.NonPublic | BindingFlags.Instance);
//получаем объект ScrollInfo
              var info = pi.GetValue(fList, null) as ScrollInfo;
//
              info.VScroll.MouseDown += new MouseEventHandler(VScroll_MouseDown);
              info.VScroll.MouseUp += new MouseEventHandler(VScroll_MouseUp);
              fList.MouseDown += new MouseEventHandler(fList_MouseDown);
              fList.TopVisibleNodeIndexChanged += new EventHandler(fList_TopVisibleNodeIndexChanged);
              fList.MouseWheel += new MouseEventHandler(fList_MouseWheel);
         }

        void fList_MouseWheel(object sender, MouseEventArgs e)
        {
            wheeled = true;
        }
        
        void VScroll_MouseUp(object sender, MouseEventArgs e)
        {
            scrollBarClicked = false;
        }

        void VScroll_MouseDown(object sender, MouseEventArgs e)
        {
            scrollBarClicked = true;
        }

        void fList_TopVisibleNodeIndexChanged(object sender, EventArgs e)
        {
            if (!wheeled&&!scrollBarClicked)
            {
                if (fList.TopVisibleNodeIndex != index)
                    fList.TopVisibleNodeIndex = index;
            }
            wheeled = false;
        }

        void fList_MouseDown(object sender, MouseEventArgs e)
        {
            index = fList.TopVisibleNodeIndex;
        }
}

Волшебство начинается в конструкторе. Примечательно что у объекта ScrollInfo есть свойство VScroll - которое является стандартным Windows.Forms ScrollBar-ом. от него мы и привязываем нужные нам события VScroll_MouseUp и VScroll_MouseDown. Так же легким движением руки в fList_TopVisibleNodeIndexChanged в условие добавляем scrollBarClicked и таки все готово. Наш класс не позволяет неизвестным потокам прокручивать самого себя без помощи пользователя. Единственное замечание касается того что если вы вдруг заходите программно изменять Scroll позицию, например с помощью TopVisibleNodeIndex то тут конечно же все решаемо добавлением какого нибудь булева public свойства для контроля в уже не раз оговоренном событии fList_TopVisibleNodeIndexChanged.

вторник, 28 августа 2012 г.

Использование TFS c нескольких компьютеров в локальной сети с единым workspace

Небольшая заметка по поводу Team Foundation Server и его знаменитых workspaces:
Если вы программируете с контролером версии от Microsoft,  естественно c EDI Visual Studio, в локальной сети на нескольких компьютерах, то у вас могут возникнуть проблемы со средой разработки т.к. на один логин и пароль от TFS вас привяжут к одному компьютеру. И синхронихировать работу над одним проектом с двух машин непредставляется возможным. Но выход таки есть:
1. Вам понадобиться создать сетевой диск на всех машинах в локальной сети которые будут использовать единые логин и пароль для TFS. Данный сетевой диск слудует привязать к папке где лежит ваш проект. В моем случае я создал сетевой диск Z: и присвоил ему всю папку work  со всеми моими проектами для единого сервера TFS.
2. Далее на каждой машине кто будет юзать этот проект от одного пользователя, следует создать   Environmental variable для пользователя. а именно: _CLUSTER_NETWORK_NAME_=<имя компьютера с которого вы всегда пользовали TFS>

И вуоля! Теперь я могу работать и на стационаре и на лаптопе с одними пользовательскими данными.

понедельник, 17 января 2011 г.

Тестовое задание от Электронные торги и безопасность, ФГУП

Чото совсем писать не хочется...

Было найдено интересное тестовое задание раскрывающее суть динамического подключения сборок на .NET платформе.

Тестовое задание звучит так:
"Разработать WinForms приложение. Основное окно должно содержать грид для отображения списка объектов. На форме должны быть кнопки: добавить, изменить, удалить. Кнопки должны открывать диалоговые формы для ввода данных, изменения данных и подтверждения удаления. Кнопка добавления должна предварительно открывать список классов объектов для выбора класса создаваемого объекта (например: физ. лицо, юр. лицо, инд. предприниматель).
Приложение не должно знать заранее список классов объектов.
Настройки хранить в файле Options.xml. Каждый класс сущности реализовать в динамически подключаемой библиотеке. В файле Options.xml указать список классов и ДЛЛ, которые их реализуют. Для каждого класса своя ДЛЛ и своя форма для создания/изменения, но одна форма на удаление объекта любого класса. Свойства объектов хранить в xml-файле. После обработки список в гриде должен обновляться."

Мне оно показалось интересным в первую очередь тем, что я редко, да и думаю что любой другой программист не часто, используют подобную фичу в своих проектах, хотя в Self подготовке к 70-536 эта тема описывается.

Ну в общем перейдем к процессу решения этой задачи. Для начала я прикинул что?где? когда? Почитал немного доков от мелколегких здесь. И решил что структура проекта будет следующая:
1. WinForms приложение - приложение подключающее библиотеки - как сущности и отображающее их объекты.
2. Связующая библиотека - аля ядро, содержащая необходимые интерфейсы для Сущности и ее класса (самое главное тут).
3. Множество различных сборок,  удовлетворяющие интерфейсам из ядра.
Немного теории о доменах (не DNS а о Application доменах) и сборках можно почитать тут .

Сказано - сделано:
где: 
DynamicObject_t - WinForms
EntityLayer - ядро
TestEntity1 и TestEntity2 - множество библиотек.

По условию задачи в WinForms приложении у нас 2-е формы:
1. Основная  - с тремя "кнопыщками" и "гридом".
2. Для выбора типа (сущности, класса) добавляемого объекта.

Снова, сказано - сделано:




Но это все косметика, самое интересное это что же из себя представляет EntityLayer.

EntityLayer содержит два интерфейса: 1. IClassEntity.cs - отображает класс для работы со сборкой, 2. - IObject.cs отображает класс объектов сборки. И класс XmlHelper.cs - содержит функционал сериализации и десериализации объектов сборки в Xml.

,где IClassEntity.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms; 

namespace EntityLayer
{
    public interface IClassEntity
    {
        string Name { get; } //Имя сборки

        Form   Edit(int ID); //Возвращает объект формы для редактирования, объекта       

        Form   Add();//Возвращает объект формы для добавления нового объекта

        void   Delete(int ID); //Удаляет объект

        List<IObject> Objects { get;}//Возвращает список объектов сборки
    }
}


,а  - IObject.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; 

namespace EntityLayer
{
   public interface IObject
    {
       int ID {get; set; }//Идентификатор объекта в сборке

       string ObjectType { get;}//Имя сборки

       string Summary { get; }  // Общее поле, возвращает суммарное значение полей объекта.

    }
}

Таким образом, получается что с помощью интерфейса IClassEntity мы можем получить общие методы взаимодействия с любыми динамически подгружаемыми сборками - классами наследуемые от данного интерфейса.
Как мы будем это делать в нашем WinForms приложении?
Для этого в этом проекте я создал класс Options.Helper.cs со статическим методом GetEntities, он считывает заказанный нам в условии Options.xml со сборками и классами и записывает уже интерпретированные классы сборок в List<EntityLayer.IClassEntity>

public static List<EntityLayer.IClassEntity> GetEntities(out bool IsError)
        {
            List<EntityLayer.IClassEntity> list = new List<EntityLayer.IClassEntity>();
            IsError = false;
            try
            {
                XElement root =  Element.Load(Properties.Settings.Default.OptionFile);               

                    var Ents = root.Elements();

                    foreach (var h in Ents)
                    {                      
list.Add(GetEntity(h.Attribute(XName.Get("class")).Value, h.Attribute(XName.Get("path")).Value));
                    }               

            }
            catch(Exception ex)
            {
                Log.WriteErrorLine(ex.ToString());
                IsError = true;
            }
            return list;

        }

Что такое GetEntity? Сейчас разберемся....

static EntityLayer.IClassEntity GetEntity(string ClassName, string
AssemblyFile)
        {
            if(!File.Exists(AssemblyFile)) throw new FileNotFoundException("Can't
find assembly file."
);
 

            Assembly assembly = Assembly.LoadFile(Application.StartupPath + "\\"
+ AssemblyFile);
 

            return (EntityLayer.IClassEntity)Activator.CreateInstance(assembly.GetType(ClassName));//Именно здесь происходит загрузка инициированного объекта класса со сборки, и оборачивание его в IClassEntity интерфейс.

        }
Ок, теперь ясно как это считывается и интерпретируется, а как выглядит класс самой подключаемой сборки? Пожалуйста...



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EntityLayer; 

namespace TestEntity1
{
   public class CompanyEntity:IClassEntity
    {  
        List<Company> Companies;//Список наших объектов, в данном случае компании

        XmlHelper<Company> helper; 

        public const string ObjectXmlFile = "TestEntity1.xml"; //Файл для сериализации и обратного процесса

        public string Name
        {
            get{ return "Company";}
        }

        public CompanyEntity()
        {
            helper = new XmlHelper<Company>(CompanyEntity.ObjectXmlFile);

            Companies = helper.Deserialise();
        }



        public System.Windows.Forms.Form Edit(int ID)
        {
            return new EditCompany(ID,Companies);//Вовращение формы для редактирования, реализуемой в данной сборке
        }     



        public System.Windows.Forms.Form Add()
        {
            return new EditCompany(null, Companies);
        }



        public List<IObject> Objects//Возвращение объектов 
        {
            get
            {
                List<IObject> result = new List<IObject>();               

                foreach(var h in Companies)
                    result.Add(h);

                return result;
            }
        }



        public void Delete(int ID)//Удаление объекта
        {
            var h = Companies.Where(i=>i.ID==ID).FirstOrDefault();
            if(h != null)
            {
                Companies.Remove(h);
                helper.Serialise(Companies);
            }
        }      

    }
}


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

Полный код проекта: SVN Тестового Проекта (как хорошо что есть дядя Гугл).

вторник, 28 декабря 2010 г.

Request for the permission of type System.Web.AspNetHostingPermission failed

Если вдруг хочется писать....я начинаю писать код.
Вот и недавно при разработке очередного ASP.NET приложения я наткнулся на интересную ситуацию:
при добавлении новой сборки элементов управления в панель,  возникала ошибка связанная с правами выполнения кода в среде .NET.

Ошибка была следующего содержания:
Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. 

В моем случае я добавлял AjaxControlToolkit.dll.

В чем может заключаться ошибка:
1. Если вы пытаетесь добавить сборку с удаленного ресурса в сети, т.е. если параметры безопасности .NET среды не настроены на доверие к данной директории, будет вылетать данная ошибка при добавлении сборки.
2. Если сборка которую вы добавляете является строго типизированной.

Как решается проблема:
В случае 1:

  •  Следует дать права удаленной директории с помощью утилиты caspol.exe: [Drive]:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\caspol.exe -m -ag 1 -url "file:////\\[Имя Компьютера]\[ИмяОбщейПапки]\*" FullTrust -exclusive on
  • Можно не напрягаясь скопировать нужную сборку к себе на компьютер и подключить ее с локального компьютера без нервотрепки.


В случае 2:
Следует добавить данную сборку в Global Assembly Cache c помощью утилиты gacutil.exe:

  • Открываете Visual Studio Command Prompt 
  • Вводите туда  gacutil /i [Полный путь до сборки]
  • Наслаждаетесь

среда, 22 декабря 2010 г.

Начало начал

Вдруг захотелось написать... о том почему я начал все это. Думаю никому не нужно объяснять для чего создаются блоги, даже напоминать не буду. Что касается данного блога, то это скорей профессиональная нужда, так сказать.

Последнее время я стал чаще задумываться на тему собственного самообразования.  Сам я по природе и по призванию IT-шник, работаю в качестве разработчика программного обеспечения на Microsoft .NET платформе. По ряду профессиональных причин приходиться часто читать различную литературу, связанную с различными процессами разработки программного обеспечения. Последнее время мне все чаще, стали попадать статьи на тематику написания собственного блога. Суть их такова, что для саморазвития необходимо писать статьи. Необходимо уметь выражать свои мысли на бумаге, уметь передать их читателю. А так как в со студенческой скамьи я ничего не писал (отчеты начальству и деловая переписка не в счет), то решил развивать данное качество в себе.