Code First и Linq to EF на примере 1С версии 7.7 и 8.3 часть I

05.10.16

Разработка - Языки и среды

Данный проект является чисто исследовательским примером использования Code First и Linq to EF на примере 1С версии 7.7. Так как сам я программист 1С, то мне всегда было интересно, как можно перенести модель объектов 1С на компилируемые языки, и использовать мощь Linq to EF. С появлением Code First давно хотел прикрутить, но все как-то руки не доходили, и вот, наконец ..

Скачать файлы

Наименование Файл Версия Размер
Обработки формирующая тексты модулей для Code First для баз 7.7 и 8.3
.zip 119,53Kb
3
.zip 119,53Kb 3 Скачать

Продолжение здесь //infostart.ru/public/402038/ 

Здесь лежит практика использования //infostart.ru/public/402433/

И примеры использования классов .Net в 1С

//infostart.ru/public/448668/

//infostart.ru/public/238584/

Для начала создадим базовый класс

public class СправочникПредок

    {

        public virtual string ID { get; set; }

        public virtual string  Наименование { get; set; }

        public virtual bool ПометкаУдаления { get; set; }

        public virtual byte ISFOLDER { get; set; }

        public virtual object ПолучитьКод() { return""; }

        public virtual string Вид() { return""; }

        public virtual bool  ЭтоГруппа() { return ISFOLDER == 1; }

    }

 

Зачем это нужно, поясню чуть позже.

На его основе можно создать описание справочника, например, Номенклатуры

 

[Table("SC84")]

    public partial class Номенклатура :СправочникПредок

    {

 

        public DateTime ДатаДляПериодическихРеквизитов = DateTime.Now;

 

        [Key]

        [Required]

        [StringLength(9)]

        override public  string ID { get; set; }

 

        [Column("CODE")]

        [Required]

        [StringLength(8)]

        public string  Код { get; set; }

 

        [Column("DESCR")]

        [Required]

        [StringLength(99)]

        override    public  string   Наименование { get; set; }

 

        [Column("PARENTID")]

        [Required]

        [StringLength(9)]

        public string   РодительId { get; set; }

        override public  byte ISFOLDER { get; set; }

 

        [Column("ISMARK")]

        override public  bool   ПометкаУдаления { get; set; }

 

   

..............................................................

        [Column("SP94")]

        [Required]

        [StringLength(9)]

        public string ОсновнаяЕдиницаId { get; set; }

        [Column("SP8906")]

        [Required]

        [StringLength(9)]

        public string  МатериалId { get; set; }

..............................................................

 

        public  override  object  ПолучитьКод() { return  Код; }

        virtual public Справочник.Номенклатура Родитель { get; set; }

 

       

        virtual public Справочник.Номенклатура Материал { get; set; }

        [InverseProperty("Материал")]

        public ICollection<Справочник.Номенклатура> ПодчиненныеДляМатериал { get; set; }

 

      

        [InverseProperty("Владелец")]

        public ICollection<Справочник.Единицы> ПодчиненныеЕдиницы { get; set; }

        override public string  Вид() { returnВидыСправочников.Номенклатура; }

 

 

 

Через атрибуты мы помечаем поля так, как нам нужно, при этом создаем свойства нужного нам типа.

Про соглашение имен подробно написано здесь

http://metanit.com/sharp/entityframework/2.6.php

 

Дополнительно обращу ваше внимание на

 

virtual public Справочник.Номенклатура Материал { get; set; }

        [InverseProperty("Материал")]

        public ICollection<Справочник.Номенклатура> ПодчиненныеДляМатериал { get; set; }

 

 

Это требует Code First для разрешения ссылок, когда один тип ссылается на сам себя и описания один ко многим. Также нужно описать коллекции на подчинённые справочники

 

public partial class  Единицы :СправочникПредок

    {

 

        public DateTime ДатаДляПериодическихРеквизитов = DateTime.Now;

 

        [Key]

        [Required]

        [StringLength(9)]

        override public  string ID { get; set; }

        [NotMapped]

        override public  string  Наименование { get { return""; } set { } }

 

        [Column("PARENTEXT")]

        [Required]

        [StringLength(9)]

        public string  ВладелецId { get; set; }

        [NotMapped]

        override public  byte ISFOLDER { get { return 2; } set { } }

 

        [Column("ISMARK")]

        override  public  bool  ПометкаУдаления { get; set; }

 

        [Column("SP79")]

        [Required]

        [StringLength(9)]

        public string  ОКЕИId { get; set; }

 

        [Column("SP76", TypeName = "numeric")]

        public decimal Вес { get; set; }

 

        [Column("SP78", TypeName = "numeric")]

        publicdecimal Коэффициент { get; set; }

 

        [Column("SP80")]

        [Required]

        [StringLength(13)]

        public  string  ШтрихКод { get; set; }

 

        [Column("SP8752", TypeName = "numeric")]

        public decimal Объем { get; set; }

 

        [Column("SP9519")]

        [Required]

        [StringLength(36)]

        public string Ref { get; set; }

 

        virtual public Справочник.Номенклатура  Владелец { get; set; }

 

        virtual public Справочник.ОКЕИ  ОКЕИ { get; set; }

        override public string Вид() { return   ВидыСправочников.Единицы; }

    }

 

У справочника единицы нет ни Наименования, ни кода и поля Родитель. Пометим эти свойства как [NotMapped] и 

установим возвращаемые значения по умолчанию

 

На этом описательная часть закончена, перейдём к самому вкусному.

Используя предка, можно написать обобщенную функцию.

 

public TEntity ПолучитьЭлементСправочника<TEntity>(string ID) where TEntity : СправочникПредок

        {

 

 

            var query2 = from спр in this.Set<TEntity>()

                         where  спр.ID == ID

                         select  спр;

            return query2.SingleOrDefault<TEntity>();

 

        }

 

Теперь мы можем вызвать её таким способом

 

public static СправочникПредок ПолучитьЗначениеНеопределенногоСправочника(string НеопределенныйИД)

        {

            var тип = НеопределенныйИД.Substring(0, 4);

            var id = НеопределенныйИД.Substring(4, 9);

 

            switch (тип)

            {

                case ВыдСправочника36.Аналоги:

                    return БДолучитьЭлементСправочника<Справочник.Аналоги>(id);

                case ВыдСправочника36.Банки:

                    return БДолучитьЭлементСправочника<Справочник.Банки>(id);

 

 

При получении неопределенного справочника нам доступны все свойства и методы предка.

Следует отметить, что Linq возвращает не реальный объект, а прокси. То есть для того, что бы получить реальный тип нужно вызвать GetType().BaseType

 А теперь посмотри на мощь LINQ.

 В Linq есть два синтаксиса запросов, которые можно совмещать

 

using (var db = new Model1())

            {

              

                var Товары = db.Спр_Номенклатура.Include("ПодчиненныеЕдиницы").Where(p => p.ISFOLDER == 2 && p.РодительId != "     0   " && p.ПодчиненныеЕдиницы.Any()).Take(10);

             

                foreach (var Товар in Товары)

                {

                   

                    Console.WriteLine("{0}.{1} - {2}", Товар.ID, Товар.Наименование, Товар.Код);

                   

                    foreach (var Единица in ТовародчиненныеЕдиницы)

                        Console.WriteLine("{0}.{1} - {2}", ЕдиницаКЕИ.Наименование, Единица.ШтрихКод, Единица.Коэффициент);

                }

           

            }

 

Такой запрос генерирует запрос к базе. Не буду указывать все поля, укажу только условия

 

)  AS [Project1]

            WHERE (2 = [Project1].[ISFOLDER]) AND (N'     0   ' <> [Project1].[PARENTID]) AND ( EXISTS (SELECT

                1 AS [C1]

                FROM [dbo].[SC75] AS [Extent2]

                WHERE [Project1].[ID] = [Extent2].[PARENTEXT]

            )) ) AS [Limit1]

        LEFT OUTER JOIN [dbo].[SC75] AS [Extent3] ON [Limit1].[ID] = [Extent3].[PARENTEXT]

 

 

 

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

 

SELECT

    [Extent1].[ID] AS [ID],

    [Extent1].[CODE] AS [CODE],

    [Extent1].[DESCR] AS [DESCR],

    [Extent1].[ISMARK] AS [ISMARK],

    [Extent1].[SP42] AS [SP42],

    [Extent1].[SP9525] AS [SP9525]

    FROM [dbo].[SC41] AS [Extent1]

    WHERE [Extent1].[ID] = @EntityKeyValue1

-- EntityKeyValue1: '     1   ' (Type = String, IsNullable = false, Size = 9)

 

 

 

Перейдем к более сложным коррелирующим запросам

 

var бд = Константы1С.ГлобальныйКонтекст.БД;

            var query = from Константа in бдаблицаКонстанты

                         where Константа.ID == 9697

                         orderby Константа.DATE descending, Константа.TIME descending, Константа.DOCID descending, Константа.ROW_ID descending

                         select Константа;

                //         select new Константы1СначениеПериодического { Значение = Константа.VALUE }).Take(1);

 

            var query2= (from спр in бдпр_ДляПериодических

                            select new

                            {

                                Наименование=спраименование,

                                ДатаСпр=спратаСпр,

                                Периодические=(from прериод in  query.Where(х=> х.OBJID==спр.ID && х.DATE<=спратаСпр).Take(1)

                                                   select new

                                                   {

                                                      Значение=прериод.VALUE,

                                                       Дата=прериод.DATE

                                                  

                                                   }).FirstOrDefault()

 

                            }

                             );

 

            foreach (var элем in query2)

            {

              

               Console.WriteLine("{0}.{1} - {2}", элем.Наименование,  элем.ДатаСпр, элем.Периодические);

 

              

            }

 

Здесь мечта каждого программиста на 8 ке и 7 ке выполняет следующий код

 

SELECT

    1 AS [C1],

    [Extent1].[DESCR] AS [DESCR],

    [Extent1].[SP9700] AS [SP9700],

    [Limit1].[ROW_ID] AS [ROW_ID],

    [Limit1].[VALUE] AS [VALUE],

    [Limit1].[DATE] AS [DATE]

    FROM  [dbo].[SC9691] AS [Extent1]

    OUTER APPLY  (SELECT TOP (1) [Project1].[ROW_ID] AS [ROW_ID], [Project1].[DATE] AS [DATE], [Project1].[VALUE] AS [VALUE]

        FROM ( SELECT

            [Extent2].[ROW_ID] AS [ROW_ID],

            [Extent2].[DATE] AS [DATE],

            [Extent2].[VALUE] AS [VALUE],

            [Extent2].[DOCID] AS [DOCID],

            [Extent2].[TIME] AS [TIME]

            FROM [dbo].[_1SCONST] AS [Extent2]

            WHERE (9697 = [Extent2].[ID]) AND ([Extent2].[OBJID] = [Extent1].[ID]) AND ([Extent2].[DATE] <= [Extent1].[SP9700])

        )  AS [Project1]

        ORDER BY [Project1].[DATE] DESC, [Project1].[TIME] DESC, [Project1].[DOCID] DESC, [Project1].[ROW_ID] DESC ) AS [Limit1]

 

 

Следует заметить, что если не использовать FirstOrDefault()

То в Периодические будет коллекция.

 

Можно применять такие запросы

 

var бд = Константы1С.ГлобальныйКонтекст.БД;

            var рез= ( from спр in бдпр_Номенклатура

                       where спридНоменклатуры==Перечисление.ВидыНоменклатуры.Услуга

                           select спр).Take(10);

 

 

Для тестов с периодическими реквизитами создал тестовый справочник.

Доступ к ним такой.

    [NotMapped]

        public DateTime ПериодДата

        {

            get

            {

                return Константы1С.Константы.ДатаДляПериодического(9693, ID, ДатаДляПериодическихРеквизитов);

            }

        }

        [NotMapped]

        public decimal  ПериодЧисло

        {

            get

            {

                 var query = Константы1С.Константы.ЗапросДляПериодическогоЗначения(9694, ID, ДатаДляПериодическихРеквизитов);

                var res = query.SingleOrDefault();

                if (res == null)

                    return 0M;

                 return decimal.Parse(res.Значение, System.Globalization.CultureInfo.InvariantCulture);

            }

        }

        [NotMapped]

        public Int64 ПериодИнт

        {

            get

            {

                var query = Константы1С.Константы.ЗапросДляПериодическогоЗначения(9695, ID, ДатаДляПериодическихРеквизитов);

                var res = query.SingleOrDefault();

                if (res == null)

                    return 0;

 

                returnInt64.Parse(res.Значение, System.Globalization.CultureInfo.InvariantCulture);

            }

        }

        [NotMapped]

        public Справочник.Номенклатура ПериодСПР

        {

            get

            {

                 var query = Константы1С.Константы.ЗапросДляПериодическогоЗначения(9696, ID, ДатаДляПериодическихРеквизитов);

 

                var db = Константы1С.ГлобальныйКонтекст.БД;

                var query2 = from спр in db.Спр_Номенклатура

                             join конст  in query on  спр.ID equals  конст.Значение.Substring(0, 9)

                             selectспр;

                return query2.SingleOrDefault();

            }

        }

        [NotMapped]

        public  string  ПериодСтр

        {

            get

            {

 

                var query = Константы1С.Константы.ЗапросДляПериодическогоЗначения(9697, ID, ДатаДляПериодическихРеквизитов);

 

                var res = query.SingleOrDefault();

                if (res == null)

                    return null;

 

 

                return res.Значение.Substring(0, 10);

            }

        }

    }

 

 

И соответственно сам запрос к периодически данным

 

publicstatic System.Linq.IQueryable<Константы1С.ЗначениеПериодического> ЗапросДляПериодическогоЗначения(int ID, string OBJID, DateTime DATE)

        {

            var бд = ГлобальныйКонтекстД;

            var query = (from Константа in бдаблицаКонстанты

                         where Константа.ID == ID && Константа.OBJID == OBJID && Константа.DATE <= DATE

                         orderby Константа.DATE descending, Константа.TIME descending, Константа.DOCID descending, Константа.ROW_ID descending

                         select  new Константы1С.ЗначениеПериодического{ Значение = Константа.VALUE }).Take(1);

 

            return query;

        }

 

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

 

 

//Пропишем тип реквизита и виртуальные свойства //В зависимости от типа поля
Функция ПолучитьТипРеквизита(вал,Стр,СтрВиртуал,IDПоля,ВидСпр="",СтрКонструктор)
    ИмяПоля=Вал.Идентификатор;
    TypeName="";
    ИмяРеквизита=ИмяПоля;
    тип=Вал.Тип;
    резулт="";
    Required=1;
    ДобавитьКолонкуДляНеопределенногоТипа=0;


    Если Тип="Строка" Тогда
        резулт="string";
        ДлинаНаименования=вал.длина;
        Если ДлинаНаименования=0 Тогда
            TypeName = "text";
            Required=0;
        Иначе
            стр="[StringLength("+ДлинаНаименования+")]"
        КонецЕсли;
    ИначеЕсли Тип="Справочник" Тогда
        резулт="string";
        ИмяРеквизита=ИмяПоля+"Id";
        Вид=СокрЛП(Вал.Вид);
        Если Вид="" Тогда
            // Это неопределенный справочник
            // Установим полю длину 13
            // И добавим свойство которого нет в базе данных
            //Будем сами его извлекать из свойства
            стр="[StringLength(13)]";
            СтрВиртуал="
            | [NotMapped]
            |public СправочникПредок "+ИмяПоля+"
            |{
            |    get
            |    {
            |        return  ГлобальныйКонтекст.ПолучитьЗначениеНеопределенногоСправочника("+ИмяРеквизита+");
            |    }
            |}";
        Иначе
            // Это определенный справочник и Code First сама сможет загружать и сохранять такие свойства
            // Установим длину поля 9 символов
            стр="[StringLength(9)]";
            резулт="string";
            СтрВиртуал=СтрВиртуал+"
            |virtual public Справочник."+Вид+" "+ИмяПоля+" { get; set; }";
        // Когда вид реквизита видом самого справочника добавим коллекцию на подчиненные элементы
            Если Вид=ВидСпр Тогда
                СтрВиртуал=СтрВиртуал+"
                |[InverseProperty("""+ИмяПоля+""")]
                |public ICollection<Справочник."+Вид+"> ПодчиненныеДля"+ИмяПоля+" { get; set; }";

                СтрКонструктор=СтрКонструктор+"
                |ПодчиненныеДля"+ИмяПоля+" = new List<Справочник."+Вид+">();";
            КонецЕсли;
        КонецЕсли;
    ИначеЕсли Тип="Документ" Тогда
        резулт="string";
        ИмяРеквизита=ИмяПоля+"Id";
        Вид=СокрЛП(Вал.Вид);
        Если Вид="" Тогда
            стр="[StringLength(13)]";
            //СтрВиртуал="
            //|[NotMapped]
            //|public object "+ИмяПоля+" { get{return 1С.КонстантыИПеречисления.ПолучитьЗначениеНеопределенногоДокумента("+ИмяРеквизита+");} }"
        Иначе

            стр="[StringLength(9)]";
            резулт="string";

            //Пока не реализован доступ к документам
            //СтрВиртуал=СтрВиртуал+"
            //|virtual public Документ."+Вид+" "+ИмяПоля+" { get; set; }"
        КонецЕсли;
    ИначеЕсли Тип="Дата" Тогда
        резулт="DateTime"
    ИначеЕсли Тип="Число" Тогда
        // Числа у нас хранятся в виде numeric
        // В зависимости от длины и дробной части можно выбрать
        //нужный тип
        TypeName = "numeric";
        Длина=вал.Длина;
        РазрядностьДробнойЧасти=вал.Точность;
        резулт=ПолучитьТипЧисловогоПоля(Длина,РазрядностьДробнойЧасти)
    ИначеЕсли Тип="Перечисление" Тогда
        стр="[StringLength(9)] //Перечисление";
        резулт="string";
        //Вид=СокрЛП(Вал.Вид);
    Иначе
        стр="[StringLength(23)] // Неопределенный тип";
        резулт="string";

        ДобавитьКолонкуДляНеопределенногоТипа=1;
    КонецЕсли;

    Если Required=1 Тогда
        Стр="[Required]
        |"+стр;
    КонецЕсли;


    Если TypeName="" Тогда
        Стр="
        |[Column("""+IDПоля+""")]
        |"+Стр;
    Иначе
        Стр="
        |[Column("""+IDПоля+""",TypeName = """+TypeName+""")]"
    КонецЕсли;

    Стр=Стр+"
    |public "+резулт+" "+ИмяРеквизита+ " { get; set; }";

    Если ДобавитьКолонкуДляНеопределенногоТипа=1 Тогда
        Стр=Стр+"
        | [Column(""T"+IDПоля+""")]
        |[Required]
        |[StringLength(3)]
        |public string Тип"+ИмяПоля+" { get; set; }";
    КонецЕсли;
КонецФункции // гл

Экспериментируйте не на рабочей базе!!!

Буду рад, если кому-то это поможет

 

Linq Code First

См. также

(Не) Строгая типизация 1С

Языки и среды Платформа 1С v8.3 Бесплатно (free)

Существует множество языков программирования, и каждый имеет свои особенности по работе с типами данных. Слабые, явные, динамические и другие... Но кто же здесь 1С и почему с приходом "строгой" типизации EDT 1С-программистам стоит задуматься над изменением своих привычек.

16.01.2024    4035    SeiOkami    21    

55

Простое приложение на Dart

Языки и среды Бесплатно (free)

Пример небольшого приложения, с которого можно начать изучать язык программирования Dart.

08.08.2023    3131    acvatoris    6    

13

Статический анализатор кода 1С на Си

Языки и среды Платформа 1С v8.3 Россия Бесплатно (free)

Написание статического анализатора для 1С традиционным способом на Си.

30.06.2023    2959    prohorp    15    

12

Сквозная задача на Исполнителе - часть первая (IMAP)

Языки и среды Абонемент ($m)

Поставили нам задачу - вынести на отдельный сервер функционал получения заказов от клиентов по электронной почте, парсинг полученных XLS в приемлемый вид и трансформация заказов в красивый JSON, понятный нашей учетной системе на 1С. Всю эту красоту желательно запустить в отдельном докер - контейнере, по возможности не тратя лицензии, поэтому отдельно стоящую конфигурацию на БСП отвергаем сразу. Можно было бы собрать всё на Apache Airflow или Apache NiFi, но решили попробовать реализовать всю логику без Open Source, будем делать свой ETL, с Исполнителем, который в версии 3.0 научился взаимодействовать с электронной почтой по IMAP. Начнем с середины - сначала напишем скрипты, а потом соберем их в рабочую конструкцию

1 стартмани

01.06.2023    1879    0    kembrik    2    

7

1С# - Расширяем код 1С кодом на C#

Языки и среды Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Вставки кода на C# внутри кода на 1С.

7 стартмани

07.04.2023    9224    4    SerVer1C    56    

42

Независимая разработка совместимых компонент на ORM 1С – миф или истина где-то в аннотациях Java?

Языки и среды Платформа 1С v8.3 Бесплатно (free)

При работе с 1С ORM (object relation mapping) все время преследует ощущение постоянного создания монолитного приложения — один раз привязался к какой либо сущности (например, справочник Контрагенты), и весь код заполнен ссылками на эту конкретную реализацию. Можно ли независимо разрабатывать в ORM совместимые между собой справочник «Контрагентов» и использующий его документ «Платежное поручение», но при этом избежать жестких зависимостей? Спасут ли нас микросервисы? Пример на аннотациях Java демонстрирует, как это возможно делать.

13.03.2023    1013    1CUnlimited    0    

2

xPath в 1С

Файловый обмен (TXT, XML, DBF), FTP Языки и среды Платформа 1С v8.3 Бесплатно (free)

Опыт работы методами языка xPath в 1С.

04.03.2023    4907    DemetrKlim    40    

46

Случай "Массив, Структура и Таблица в JS плюс верстка техдемо"

WEB-интеграция Языки и среды Платформа 1С v8.3 Абонемент ($m)

Добавление в JS обозревателя 1Сных коллекций массив, структура и таблица значений. Строкование в json формат для обмена с 1Сным сервером.Убедимся, что в обозревателе 1Сные коллекции действительно работают через чтение, изменение и строкование объекта обратно в json строку.

10 стартмани

01.03.2023    2044    Steelvan    0    

5
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. Serginio 938 31.08.15 14:31 Сейчас в теме

Вот какие левые запросы с условием на неравенство получились

var бд = Константы1С.ГлобальныйКонтекст.БД;

            var qr = from Номенклатура in бд.Спр_Номенклатура 
                     from единицы in бд.Спр_Единицы.Where(единица => единица.ВладелецId == Номенклатура.ID && единица.ШтрихКод.CompareTo("4") > 0).DefaultIfEmpty() 
                     select new { Номенклатура.Наименование,
                         Номенклатура.ПолнНаименование,
                         единицы.ШтрихКод,
                         ОКЕИ=единицы.ОКЕИ.Наименование
                     };



            foreach (var элем in qr.Take(1000)) 
            { 

                if (элем.ШтрихКод == null)
                    continue;

                Console.WriteLine("{0}.{1} - {2}", элем.Наименование.TrimEnd(), элем.ШтрихКод == null ? "null" : элем.ШтрихКод, элем.ПолнНаименование.TrimEnd(), элем.ОКЕИ == null ? "null" : элем.ОКЕИ);


            }

Показать


Выдает такой запрос
SELECT 
    [Limit1].[C1] AS [C1], 
    [Limit1].[DESCR] AS [DESCR], 
    [Limit1].[SP101] AS [SP101], 
    [Limit1].[SP80] AS [SP80], 
    [Limit1].[DESCR1] AS [DESCR1]
    FROM ( SELECT TOP (1000) 
        [Extent1].[DESCR] AS [DESCR], 
        [Extent1].[SP101] AS [SP101], 
        [Extent2].[SP80] AS [SP80], 
        [Extent3].[DESCR] AS [DESCR1], 
        1 AS [C1]
        FROM   [dbo].[SC84] AS [Extent1]

        LEFT OUTER JOIN [dbo].[SC75] AS [Extent2] ON ([Extent2].[PARENTEXT] = [Extent1].[ID]) AND ([Extent2].[SP80] > N'4') 
        LEFT OUTER JOIN [dbo].[SC41] AS [Extent3] ON [Extent2].[SP79] = [Extent3].[ID] 
    )  AS [Limit1]


Показать
Или больше мне нравится таой запрос

var бд = Константы1С.ГлобальныйКонтекст.БД;
            var qr = from Номенклатура in бд.Спр_Номенклатура

                     select new
                     {
                         Номенклатура.Наименование,
                         Номенклатура.ПолнНаименование,
                         Единицы = (Номенклатура.ПодчиненныеЕдиницы.Where(единица => единица.ШтрихКод.CompareTo("4") > 0)
                         .Select(единица =>

                         new
                         {
                             единица.ШтрихКод,
                             ОКЕИ = единица.ОКЕИ.Наименование
                         }
            )
                         )
                     };


            foreach (var элем in qr.Take(1000))
            {


                foreach (var единица in элем.Единицы)
                    Console.WriteLine("{0}.{1} - {2}", элем.Наименование.TrimEnd(), единица.ШтрихКод, элем.ПолнНаименование.TrimEnd(), единица.ОКЕИ);


            }

Показать

Генерирут SQL запрос


SELECT 



    [Project2].[ID] AS [ID], 
    [Project2].[C1] AS [C1], 
    [Project2].[DESCR] AS [DESCR], 
    [Project2].[SP101] AS [SP101], 
    [Project2].[C2] AS [C2], 
    [Project2].[SP80] AS [SP80], 
    [Project2].[DESCR1] AS [DESCR1]
    FROM ( SELECT 
        [Limit1].[ID] AS [ID], 
        [Limit1].[DESCR] AS [DESCR], 
        [Limit1].[SP101] AS [SP101], 
        [Limit1].[C1] AS [C1], 
        [Join1].[SP80] AS [SP80], 
        [Join1].[DESCR] AS [DESCR1], 
        CASE WHEN ([Join1].[PARENTEXT] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
        FROM   (SELECT TOP (1000) 
            [Extent1].[ID] AS [ID], 
            [Extent1].[DESCR] AS [DESCR], 
            [Extent1].[SP101] AS [SP101], 
            1 AS [C1]
            FROM [dbo].[SC84] AS [Extent1] ) AS [Limit1]
        LEFT OUTER JOIN  (SELECT [Extent2].[PARENTEXT] AS [PARENTEXT], [Extent2].[SP80] AS [SP80], [Extent3].[DESCR] AS [DESCR]
            FROM  [dbo].[SC75] AS [Extent2]
            INNER JOIN [dbo].[SC41] AS [Extent3] ON [Extent2].[SP79] = [Extent3].[ID] ) AS [Join1] ON ([Limit1].[ID] = [Join1].[PARENTEXT]) AND ([Join1].[SP80] > N'4')
    )  AS [Project2]
    ORDER BY [Project2].[ID] ASC, [Project2].[C2] ASC
Показать

Оставьте свое сообщение