Переключение на Главную Страницу Страницы: [1] 2  ОтправитьПечать
Горячая тема (более 10 ответов) Code First и Linq to EF на примере 1С (число прочтений - 4472 )
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Code First и Linq to EF на примере 1С
28. Августа 2015 :: 13:06
Печать  
Хочу сразу же выразить огромную благодарность разработчикам 1С++ и хочу поделиться своим видением работы с 1С через
Code First и Linq to EF
Данный проект  является чисто исследовательским использования Code First и Linq to EF на примере 1С версии 7.7. Так как сам я программист 1С, то мне всегда было интересно как можно перенести модель объектов 1С на компилируемые языки, и использовать мощь Linq to EF.
С появлением Code First давно хотел прикрутить, но все как то руки не доходили и вот наконец ..

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

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 publicstring 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  virtual  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ВидыСправочников.Единицы; }
    }


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

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

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


           var query2 = from спр in this.Set<TEntity>()
                        where  спр.ID == ID
                        select  спр;
           return query2.SingleOrDefault<TEntity>();

       }
  

CodeFirstTo1C.zip ( 59 KB | Загрузки )
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #1 - 28. Августа 2015 :: 13:07
Печать  
Теперь мы можем вызвать её таким способом

public static СправочникПредок ПолучитьЗначениеНеопределенногоСправочника(string НеопределенныйИД)
       {
           var тип = НеопределенныйИД.Substring(0, 4);
           var id = НеопределенныйИД.Substring(4, 9);

           switch (тип)
           {
               case ВыдСправочника36.Аналоги:
                   return БД.ПолучитьЭлементСправочника<Справочник.Аналоги>(id);
               case ВыдСправочника36.Банки:
                   return БД.ПолучитьЭлементСправочника<Справочник.Банки>(id);


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

А теперь посмотри на мощь 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);


  
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #2 - 28. Августа 2015 :: 13:11
Печать  
Для тестов с периодическими реквизитами создал тестовый справочник.

Доступ к ним такой.
   [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;

               return Int64.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);
           }
       }
   }


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

public static 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;
       }


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

Code Firstработает на MS SQL 2005 и выше. Это нужно учитывать, так как 1С 7.7 заточена под 2000


Для создания модели сначала запустите из 1С CodeFirstTo1C.ert
Для её работы нужно скачать 1CPP.dll отсюда
http://www.1cpp.ru/index.php/File:Icpp-latest.rar#.D0.92.D0.B5.D1.80.D1.81.D0.B8....
Пользуясь случаем хочу выразить огромную благодарность содателям этого чудного проекта

Установить  НаименованиеБазы и namespace

В итоге будут показаны тексты модулей которые нужно будет скопировать в VS.
В VS создайте проект с именем которое указали в namespace.

В проекте выберите добавить класс, назовите его на ваше усмотрение и скопируйте из таблицы текст в него, предварительно переключив клавиатуру на RU.

После создания всех модулей перейдите в управление пакетами и установите
Entity Framework

Про строку подключения можно посмотреть здесь
http://professorweb.ru/my/entity-framework/6/level2/2_9.php

Но есть еще вариант посмотреть как реально работает Code First к существующей базе.
http://metanit.com/sharp/entityframework/2.1.php

Вы получите строку подключения и описание какой либо таблицы как я и делал на примере
И заменить в секции connectionStrings на своё имя
  

CodeFirstTo1C_001.zip ( 59 KB | Загрузки )
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #3 - 29. Августа 2015 :: 05:33
Печать  
Выложил статью http://infostart.ru/public/393228/
  
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #4 - 31. Августа 2015 :: 08:41
Печать  
Здесь http://www.forum.mista.ru/topic.php?id=751931
добавил примеры использования Linq и какие SQL запросы генерятся
  
Наверх
 
IP записан
 
trdm
1c++ power user
qt1l developer
1c++ moderator
Отсутствует



Сообщений: 2343
Местоположение: г. Ростов-на-Дону
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #5 - 31. Августа 2015 :: 13:57
Печать  
Ну это как отправляясь на рыбалку с рюкзаком и удочкой, тащить с собой на тачке дизель генератор для фонарика.
  
Наверх
IP записан
 
berezdetsky
1c++ power user
Отсутствует


barba non facit sisadminum

Сообщений: 1986
Местоположение: Москва
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #6 - 31. Августа 2015 :: 14:01
Печать  
Ретроград.  Улыбка Это круто, на самом деле. Лет бы пять-шесть назад..
  

пароль как коньяк, чем больше звездочек, тем лучше
Наверх
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #7 - 31. Августа 2015 :: 14:18
Печать  
Если тебе нужен доступ например из asp.net или нужна интеграция с другими приложениями, то как раз ничего лишнего тащить не надо.

А что касается лет 5, то Code First где то в 2011 появился, раньше был Linq to SQL. Но руки как то не доходили. Там дел на самом деле не много, но вот так.
А так он пока для того, что бы посмотреть на Code First и Linq to EF.
А нужно это или нет каждый делает выводы для себя. Мне лично очень понравился Linq to EF
  
Наверх
 
IP записан
 
trdm
1c++ power user
qt1l developer
1c++ moderator
Отсутствует



Сообщений: 2343
Местоположение: г. Ростов-на-Дону
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #8 - 31. Августа 2015 :: 14:26
Печать  
berezdetsky писал(а) 31. Августа 2015 :: 14:01:
Ретроград.  Улыбка Это круто, на самом деле. Лет бы пять-шесть назад..

Да ладно.
Сколько это все весит?
Уже ушла пора когда перделки весили килобайты...
  
Наверх
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #9 - 31. Августа 2015 :: 14:39
Печать  
А ты попробуй. Я для этого и сделал, что бы оценить, посмотреть и попробовать
  
Наверх
 
IP записан
 
trdm
1c++ power user
qt1l developer
1c++ moderator
Отсутствует



Сообщений: 2343
Местоположение: г. Ростов-на-Дону
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #10 - 31. Августа 2015 :: 14:45
Печать  
Serginio писал(а) 31. Августа 2015 :: 14:39:
А ты попробуй. Я для этого и сделал, что бы оценить, посмотреть и попробовать

Да ну, из-за одной хотелки столько кода городить, а потом по куче компов безать. Я предпочитаю менее геморойные подходы.
  
Наверх
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #11 - 31. Августа 2015 :: 15:24
Печать  
Ну он автоматически городится. А попробовать одного компа хватает.
  
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #12 - 17. Сентября 2015 :: 09:36
Печать  

Выложил тестовую конфигурации с обработкой http://files.rsdn.ru/19608/CodeFirstTo83.zip

Большая просьба выложить или отослать архив SQL так как у меня только файловые. Поэтому сделал вслепую. Буду рад если кто протестирует.
  

CodeFirstTo83.zip ( 49 KB | Загрузки )
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #13 - 18. Сентября 2015 :: 12:33
Печать  
Подправил  обработку формирования классов
  

CodeFirstTo83_001.zip ( 50 KB | Загрузки )
Наверх
 
IP записан
 
Serginio
Junior Member
**
Отсутствует


1C++ rocks!

Сообщений: 17
Зарегистрирован: 21. Июня 2012
Пол: Мужской
Re: Code First и Linq to EF на примере 1С
Ответ #14 - 24. Сентября 2015 :: 13:54
Печать  
Выложил статью http://catalog.mista.ru/public/402038/
Прикрепил файлы с обработками формирования C# файлов
  

CodeFirstTo1C_002.zip ( 114 KB | Загрузки )
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: [1] 2 
ОтправитьПечать