Страницы

Поиск по вопросам

суббота, 15 февраля 2020 г.

Индексация сайта на vue.js

#javascript #vuejs


Здравствуйте. Вопрос, возможно, глупый, но меня, как новичка, это очень интересует.
Предположим, я использую vuejs для интернет магазина. Товар выводится с помощью JS,
фильтрация идет с помощью vue. Я так понимаю, что тогда будет проблема с индексацией
сайта. Так как робот не сможет нормально проиндексировать то, что появляется после
загрузки страницы.
Как в таком случае быть? Может я неправильно понимаю логику применения vue и подобных
framework? 
    


Ответы

Ответ 1



Современные поисковые движки хорошо заточены под индексирование более менее-статического контента. Поэтому с индексированием Single Page Applications, которые вообще целиком js-динамика у них трудности. И это проблема. Как для поисковиков так и для разработчиков. Со стороны разработчиков самым распространенным подходом является так называемый SSR - server side rendering. Это когда каркас приложения (значительная часть html-разметки) генерируется на сервере, потом отдается на клиент, соответственно поисковик может ее нормально проиндексировать. И на клиенте этот каркас обычно "оживляется" всякими дополнительными обработчиками и прочей динамикой в зависимости от действий юзера. При этом кусочек серверной части приложения ответственной за рендеринг написан с использованием обычно примерно того же что и клиентской части. React там или vuejs. Поддержка SSR есть в большинстве современных js-фреймворков. Конкретно у vue.js есть мануал на русском языке про SSR. Там много написано про SSR в общем, не только конкретно про vue. Если вопросы SEO стоят прям очень жестко, то нужно учесть что поисковики пытаются решить проблему со своей стороны все лучше обучая своих ботов понимать динамику, и могут занижать в выдаче сайты с SSR. Но где-то год назад на родные механизмы полагаться было сложно и SSR был гораздо лучшим способом чем тонкая настройка под краулеры.

Не получается создать exe файл из файла .py используя cx freeze на Python 3.6

#python #cx_freeze


Файл создается но при открытие вылетают ошибки и файл закрывается


пробовал через py2exe но файл даже не создался, может кто что подскажет 

from cx_Freeze import setup, Executable
packages = ["vk","requests","json","urllib.request","os","time"]
build_exe_options = {
"packages": packages,
"includes": packages,
'include_files': [],
'zip_include_packages': "*",
'zip_exclude_packages': None,
'include_msvcr': True,
'constants':{}
}
setup(
  name = "reer",
  version = "0.1",
  description = "Blackjack",
  options={"build_exe": build_exe_options},
  executables = [Executable("reer.py")]
)

    


Ответы

Ответ 1



Добавь список пакетов в файле setup.py для cx_freeze. packages = ["os","utils","struct","mschap","pytz","motor","aiohttp","asyncio","sms"] Этот список положишь в параметры, например дальше у меня так: build_exe_options = { "packages": packages, "excludes": excludes, "includes": packages, 'include_files': [], 'zip_include_packages': "*", 'zip_exclude_packages': None, 'include_msvcr': True, 'constants':{} } и передаём в cx_setup( name = "project", version = "0.1", description = "project build", options = {"build_exe": build_exe_options}, executables = executables, data_files = ['config.json'] ) В твоем случае пакет inda плохо собирается - надо добавить его.

Классический стиль программы в Delphi XE 10.2

#delphi #vcl


Можно ли в Delphi XE 10.2 Tokyo сделать классический стиль VCL контролов на форме,
как в Delphi 7 без XP-манифеста?

То есть нужен вариант как слева:


    


Ответы

Ответ 1



Всё оказалось до боли просто, нужно убрать галочку: Project -> Options... -> Application -> Enable Runtime Themes

Ярлык или батник в Linux

#linux #ubuntu #lubuntu


Начал изучать Linux. Установил Lubuntu 16.04. У меня есть росшаренная папка на Windows-машине.
Как мне написать скрипт (типа DOS-батника) и разместить его на рабочий стол Lubuntu,
что бы по необходимости, нажать на него и выполнится команда:

mount.cifs //192.168.1.2/general /mnt/general -o username=user,password=12345


и такой же самый "ярлык" с командой:

umount /mnt/general


Та и вообще как создавать ярлыки на рабочий стол, например для офисного документа
(электронная таблица). Что бы "далекий" пользователь могла сразу его запускать? 
Заранее спасибо.
    


Ответы

Ответ 1



для выполнения команды надо создать файл с произвольным именем и суффиксом .desktop такого минимального содержимого (возможно, некоторые строки даже лишние): [Desktop Entry] Name=какое-нибудь имя Exec=команда (с параметрами) Terminal=false Type=Application чтобы он появился на «рабочем столе» некоего пользователя, надо поместить его в соответствующий каталог. путь к этому каталогу можно получить, выполнив от имени целевого пользователя команду: $ xdg-user-dir DESKTOP пример вывода: /home/user/Desktop вроде бы, нынче некоторые «особо умные» de (desktop environments) начинают «вставлять палки в колёса», сообщая какой-то бред про «запуск недоверенной программы» (или что-то в этом духе), если у данного файла не стоит битов исполнимости. потому, на всякий случай, лучше их поставить: $ chmod +x файл

Ответ 2



У меня kubuntu, так что для Lubuntu детали могут отличаться. На рабочем столе нажать правой кнопкой, выбрать Создать -> Текстовый файл. В этом файле вводим текст #!/bin/sh sudo mount.cifs //192.168.1.2/general /mnt/general -o username=user,password=12345 Сохранить, закрыть. Далее на этом файле нажимаем правой кнопкой мыши, выбираем Свойства. На вкладке Права выставляем флаг Является выполняемым. В общем все. У меня заработало (команда конечно же была другая). Ярлыки для документов на рабочем столе создаются также как и в Windows, при помощи технологии Drag&Drop (перетаскивание мышью). У меня для создания ярлыка, а не перемещения файла, потребовалось нажать кнопку Alt.

Как выполнить код после загрузки и отображения usercontrol

#vbnet #usercontrol


На форме есть кнопка, при нажатии на которую отображается usercontrol. Если в метод
click кнопки поместить процедуру, то она будет выполняться, затем выход из метода click
и только тогда появляется usercontrol. 

А как сделать чтобы сначала показался usercontrol, а затем начал выполняться код
процедуры?
    


Ответы

Ответ 1



Код нужно поместить в следующую процедуру в самой форме: Private Sub UserForm_Activate() MsgBox "Run after activation (show) of form " End Sub Если вы не хотите чтобы процедура была внутри класса контроля, то создайте процедуру общего доступа (Public) в любом модуле и тоже вызывайте ее из процедуры активации UserForm_Acivate. .. В модуле: Public Sub DoSomethingAfterControlShow() MsgBox "Ok After show" End Sub .. В UserForm: Private Sub UserForm_Activate() DoSomethingAfterControlShow End Sub

Ответ 2



Если я правильно понимаю суть вопроса, то код Private Sub btnShow_Click(sender As Object, e As EventArgs) Handles btnShow.Click MsgBox("Hi") btnUnvisible.Visible = True End Sub выполняется синхронно и контрол btnUnvisible появляется только после нажатия на кнопку "Ок" диалогового окна. Для того, чтобы любой код (в моем случае это msgbox, но может быть вызов любой другой процедуры) выполнялся асинхронно, нужно переписать обработчик события следующим образом: Private Async Sub btnShow_Click(sender As Object, e As EventArgs) Handles btnShow.Click Dim t = New Task(Sub() MsgBox("Hi") ' здесь может быть вызов процедуры или любой другой код End Sub) t.Start() btnUnvisible.Visible = True Await t End Sub В данном случае код внутри Task будет выполнен асинхронно (в другом потоке или в этом же, в зависимости от более оптимального расклада с точки зрения пула потоков), весь код между объявлением объекта Task и ожиданием завершения асинхронной процедуры Await t будет выполнен "параллельно" - таким образом, при нажатии на кнопку будет одновременно отображен другой контрол и показано диалоговое окно

Как в js сделать смену фона при активности элемента

#javascript #css #site


У меня есть слайдер с текстом. Там 6 элементов li. Мне нужно чтобы при активности
первого элемента менялся фон блока всего, затем при активности второго менялся фон
уже на другой и т.д 
    


Ответы

Ответ 1



Приблизительно такое решение на jQuery. Можете изменять его соответственно вашей вёрстке. UPD: упростил решение, вместо функции используется массив. var images = [ 'https://yandex.ru/images/today?size=1920x1080', 'http://www.carscope.ru/piclib/1280/960/91699.jpeg', 'https://ru9.anyfad.com/items/t1@a8c329ba-9f5c-40d1-92eb-b43b0d6edb37/Blyuda-iz-tykvy-recepty.jpg' ]; $('document').ready(function() { $('.content').css('background-image', 'url('+images[$('li.active').index()]+')'); }); $('.content li').click(function() { $('.content li').each(function() {$(this).removeClass('active');}) $(this).addClass('active'); $('.content').css('background-image', 'url('+images[$(this).index()]+')'); }); .active a { color: red; } .content { width: 500px; height: 500px; }

Общий член для наследуемых классов

#c_sharp


Имеется абстрактный (базовый) класс (для примера):

public abstract class ClassBase
{
    protected static Logger logger = new Logger("logger_name");
}


И два потомка:

public class Class1 : ClassBase {}

public class Class2 : ClassBase {}


У потомков должен быть общий логгер. Logger реализует IDisposable. Как быть в таком
случае? Мы же ведь не может выполнить Dispose на данном члене
    


Ответы

Ответ 1



Никак. Статические поля не нужно Dispose()-ить, ведь они будут использованы другими экземплярами класса, создаваемыми после окончания работы Dispose() данного экземпляра. Возможно, вам понадобится «вручную» закрыть логгер в конце работы программы, в тот момент, когда вы можете гарантировать, что экземпляров данного класса (и его потомков) больше нет. Более правильное решение — изменить дизайн логгера, чтобы он не требовал вызова Dispose() в конце работы.

Необходимость валидации сущностей, извлеченных из базы данных

#c_sharp #валидация #entity #ddd


Всем доброго времени суток)
Разрабатываю систему с использованием DDD.

В системе есть 2 модуля:


Модуль конфигурации. Он занимается регистрацией новых устройств: само устройство,
его модель и прочие параметры.
Модуль проверки устройства на дефекты.





Каждый тип устройства описывает конкретный класс-сущность (entity). При этом при
создании объекта класса-сущности необходимо производить валидацию его параметров. Регистрация
нового устройства в системе заканчивается его сохранением в базе данных с использованием
соответствующего репозитория (repository).
В модуле проверки выбранное из списка устройство загружается в программу из базы
данных. При помощи соответствующего репозитория на основе идентификатора возвращается
устройство для проверки на неисправности. На выходе репозитория в диагностику загружается
соответствующий объект класса-сущности устройства. Однако для создания такого объекта
необходимо проводить его валидацию. 


Возникает вопрос: насколько необходимо проводить валидацию извлеченной из базы данных
информации, если изначально в базу данных записывается верная информации о сущности?
Сама валидация осуществляется классом-валидатором, встраивающимся в соответствующий
объект-сущность устройства.

Может быть я что-либо совсем не так понимаю?
    


Ответы

Ответ 1



В базе нужно хранить уже достоверную и правильную информацию, чтобы сохранялась целостность данных. То есть что ты именно будешь верифицировать при извлечении из бд, я не понимаю. Проверять данные на корректность, нужно обязательно (!!) перед сохранением в БД.

Ответ 2



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

new Date() не обновляет значения

#javascript #jquery #form #localstorage


Почему new Date возвращает одни и те же значения(одно и тоже время)?
Нужно чтоб при добавление сообщения ему возвращалась соответствующие время(при котором
он был добавлен).
В коде ниже выводятся сообщения с одним и тем же значением времени, пока страницу
не перезагрузишь.

В чем дело? я что то не так сделал или так и должно быть?





var time = new Date();
var timeS = time.toISOString();

$("#add-text").click( function() {

  var message = $("#text").val();

  $("#chat").append("

" + message + "

"); $("#form")[0].reset(); var chat = $("#chat").html(); localStorage.setItem("chat", chat); return false; }); if(localStorage.getItem("chat")) { $("#chat").html(localStorage.getItem("chat")); }


Ответы

Ответ 1



Объявляйте время при клике $('#add-text').click(function(){ var time = new Date(); var timeS = time.toISOString(); /*Далее ваш код*/ })

Ответ 2



у тебя уже получается сначала дата создается и присваивается в момент инициализации переменной var time = new Date(); var timeS = time.toISOString(); и его ты уже используешь Если я правильно понял то тебе надо инициализировать переменную в момент вызова этой переменой. Как показал @DarkSir при клике

Горячие клавиши Visual Studio

#c_sharp #wpf #hotkeys


Столкнулся с проблемой, что в проекте WPF неудобно работать на пол окна с редактором
XAML и пол окна с конструктором. Сделал отображение на весь экран, но теперь, чтобы
перейти с конструктора на редактор и обратно, нужно постоянно кликать мышью.

С редактора на конструктор можно перейти Shift + F7. 

Кто-нибудь знает горячую клавишу для обратного действия? Ctrl + Alt + 0 переводят
на код, а нужно именно на XAML.
    


Ответы

Ответ 1



Shift + F7 Делает то что нужно, т.е. работает как Toggle. Туда и обратно переключает.

Для чего нужны memcached и OPcache и стоит ли использовать их вместе в PHP7?

#php


Привет. Собственно, вопрос, стоит ли использовать memcached и Opcache вместе на PHP7?
Почувствую ли я какой-то прирост производительности? Как гласит ответчик в теме на
англоязычном StackOverflow: 


  OPcache is for accelerating code access


Что это значит? Ускорение доступа к коду? Или ускорение доступа кода к чему-то?


  memcached is for accelerating data access


memcached нужен для ускорения доступа к данным. К каким?
    


Ответы

Ответ 1



Как обычно выполняется PHP скрипт? PHP открывает файл с кодом, компилирует его, затем выполняет. Поскольку файлов может быть много, процесс их открытия, чтения и компиляции может отнимать кучу времени и ресурсов. Если файлы не меняются, то постоянную компиляцию можно не делать. Лучше сделать ее один раз и закэшировать результат. Именно это и делает модуль opCache. Результат первой компиляции будет сохранен в кэш, с которым и будет работать PHP. Таким образом это ускорит выполнение за счет отсутствия тяжелого процесса компиляции. Когда файлы изменяются, модуль сам сбросит кэш и обеспечит перекомпиляцию. Короче, этот модуль делает очень полезную экономию ресурсов даже без необходимости его как-то настраивать. Чем сложнее приложение, тем выше эффективность этой оптимизации. Часто код просто медленный Например, обращения к внешним API, тяжелые выборки из баз данных, обработка больших файлов может занимать продолжительное время. В этом случае, кэширование данных следует использовать, как средство оптимизации и в этом вам поможет memcached, который поможет сохранить часто получаемые результаты. Например сложная выборка из БД, которая пригодится многим пользователям, но очень сильно напрягает базу данных, хотя почти для всех одинакова с минимальными отличиями. Memcached спроектирован так, чтобы все его операции имели алгоритмическую сложность O(1), т.е. время выполнения любой операции не зависит от количества ключей, которые хранит memcached. Естественно, нужно учитывать, что архитектура предполагает потерю данных в случае сбоя. Результат OpCache -> сохраняет опкоды для увеличения производительности исполнения кода -> данные регулируется непосредственно PHP и конфигурацией Memcached -> необходим для сохранения часто используемых данных и следовательно увеличивает скорость получения данных -> записываемые данные и их ключи регулируются разработчиком, который использует данное программное обеспечение Полезные ссылки Обзор OpCache и алгоритма работы Обзор Memcached и алгоритма работы

Как правильно выложить портфолио на GitHub?

#github #frontend


Как лучше разместить своё портфолио на GitHub? Вот например я недавно сверстал сайт,
прикрутил мини сервер который наполняет его товаром, но на GitHub ведь сервер не запустишь,
а если запускать просто HTML страницу, то не будет товара и некоторых функций. 

Или стоит просто в описание добавить чтобы качали Git и запускали у себя? А прямо
на GitHub просто сверстанную страницу показать?
    


Ответы

Ответ 1



GitHub — это портфолио вашего кода. Туда потенциальный работодатель может посмотреть, чтобы понять, какой код вы пишете, в каких проектах участвуете. Для портфолио с динамическими сайтами нужен традиционный хостинг. Для нетребовательных демонстрационных сайтов без каких-либо посетителей хватит самых дешёвых хостингов (возможно, даже бесплатных). GitHub хостит только статические странички. Отмечу, что по возможности стоит демонстрировать реальные работающие проекты, в которых вы приняли участие, а не поделки на коленке. Если вы кодируете back-end, то гораздо интереснее будет код. Если вы кодируете front-end или дизайните страницы, то работающий сайт уже будет важен.

Ответ 2



Не думаю, что показывать работодателю "интернет-магазин" это хорошая идея. Существует множество таких же, но уже готовых решений, к примеру, тот же VirtueMart. Кроме того, никто не любит вчитываться, язык бизнеса это наглядность. Маловероятно, что кто-то дойдет дальше регистрации на подобном ресурсе. В этом репозитории автор поднимает вопрос неверного оформления большинства портфолио программистов на Github Pages. Повсеместное использование Bootstrap с наполнением текстом-рыбой делает написание очередной поделки контрпродуктивным. Как решение проблемы предлагается создать PWA с максимальной компактностью, приметностью и актуальными использованными инструментами.

Как сделать закрашивание SVG иконки на hover?

#html #css #svg #hover #png


Есть две иконки: 1-ая (иконка сверху) стоит обычная, но при ховере она должна закрашиваться
(иконка снизу). Как такое реализовать, да еще и плавно, желательно?

P.S. Вариант только создать две png иконки и подменять при ховер ? 









    


Ответы

Ответ 1



Чтобы при ховере изменять цвет заливки, используйте свойство fill-opacity и, при необходимости, stroke-opacity: svg { display: block; width: 200px; height: 162px; margin: 40px auto; } path { fill-opacity: 0; fill: #f00; stroke: #f00; transition: all 0.4s linear; } svg:hover path { fill-opacity: 1; stroke-opacity: 0; }

Ответ 2



Самый простой способ с двумя картинками друг над другом и изменением прозрачности верхней: div { width: 50px; height: 50px; background: url('https://i.stack.imgur.com/G1BtB.jpg') no-repeat -20px -14px; overflow: hidden; } .item:hover { opacity: 0; transition: all 500ms ease; } .item-wrapper { background-position: -20px -88px; }


C# проверка на null поля в в LINQ запросе

#c_sharp #entity_framework #linq


Имеется следующий код:

IQueryable users = from c in db.UserSet
                                               where c.Date == dateTimePicker1.Value &&
                                                     c.FirstName == textBox2.Text &&
                                                     c.LastName == textBox3.Text &&
                                                     c.Patronym == textBox4.Text &&
                                                     c.City == textBox5.Text &&
                                                     c.Country == textBox6.Text
                                               select c;


Помогите реализовать проверку на null в каждом поле. То есть если сейчас хотя бы
одно поле будет пустым, то запрос выдаст 0 строк независимо от других условий. Как
можно сделать так, чтобы пустые поля игнорировались, и запрос выполнялся по остальным
условиям?

Конечно можно сделать проверку на уровне if (textbox1.text!=null){...} но я так понимаю
это много лишнего кода. 
    


Ответы

Ответ 1



Привет. Думаю можно так: IQueryable users = from c in db.UserSet where (c.Date == dateTimePicker1.Value || dateTimePicker1.Value == null) && (c.FirstName == textBox2.Text || textBox2.Text == null) && (c.LastName == textBox2.Text || textBox2.Text == null) && (c.Patronym == textBox3.Text || textBox3.Text == null) && (c.City == textBox4.Text || textBox4.Text == null) && (c.Country == textBox5.Text || textBox5.Text == null) select c;

Ответ 2



Можно еще так: Если исходить из того, что условная конструкция будет представлена в виде простой бинарной операции, для которой необходимо проверить, что значением правого операнда не является значение по умолчанию. Тогда можно воспользоваться деревьями выражений, а саму проверку вынести в отдельный метод расширения. public static class EfExtension { public static IQueryable WhereIfRightIsNotDefault(this IQueryable source, Expression> predicate) where T : class { var body = predicate.Body as BinaryExpression; if (body == null) return source; // Получаем значение правого операнда. var currentValue = Expression.Lambda(body.Right) .Compile().DynamicInvoke(); // Получаем значение по умолчанию для типа правого операнда. var defaultValueForType = GetDefaultValueForType(body.Right.Type); // Сравниваем текущее значение со значением по умолчанию. var isEquals = EqualityComparer.Default .Equals(currentValue, defaultValueForType); // Если текущее значение не равно значению по умолчанию. if (!isEquals) { // Добавляем условие в предложение where. source = source.Where(preducate); return source; } return source; } // Возвращает значение по умолчанию для конкретного типа. private static object GetDefaultValueForType(Type type) { var p = Expression.Convert(Expression.Default(type), typeof(object)); return Expression.Lambda>(p).Compile()(); } } Использование: var users = db.UserSet .WhereIfRightIsNotDefault(p => p.Date == dateTimePicker1.Value) .WhereIfRightIsNotDefault(p => p.FirstName == textBox2.Text) .WhereIfRightIsNotDefault(p => p.LastName == textBox3.Text) .WhereIfRightIsNotDefault(p => p.Patronym == textBox4.Text) .WhereIfRightIsNotDefault(p => p.City == textBox5.Text) .WhereIfRightIsNotDefault(p => p.Country == textBox6.Text).ToList()

Как преобразовать String параметр в нужный тип? [дубликат]

#java


        
             
                
                    
                        
                            На этот вопрос уже даны ответы здесь:
                            
                        
                    
                
                        
                            Динамически создаваемый объект и обращение к методу
                                
                                    (3 ответа)
                                
                        
                                Закрыт 2 года назад.
            
                    
Есть метод, который принимает в качестве параметра имя класса, но как String. Как
можно преобразовать этот String в нужный класс?

public void doStuff(String clazzName) {
     clazzName.doMore();
}

    


Ответы

Ответ 1



Class clz = Class.forName(clazzName); Например: Class clz = Class.forName("java.lang.Integer");

startManagingCursor() is deprecated, метод перечеркнут и не работает. Почему?

#java #android


    Cursor cursor=db.getCompanyData();

    startManagingCursor(cursor);


Так же далее не работает метод SimpleCursorAdapter
    


Ответы

Ответ 1



Этот метод помечен как устаревший(deprecated). Используйте CursorLoader. Подробнее о CursorLoader

Ответ 2



Добавлю еще 5 копеек: Activity.startManagingCursor() хоть и deprecated, но должен работать. Просто им не рекомендуется пользоваться потому что он выполняется в UI треде и замедляет работу интерфейса. Возможно в последних версиях Android он уже и не работает, но до 19-20 версии он точно работал. Для SimpleCursorAdapter есть специальный конструктор, который не deprecated (введен в API=11) Вообще, конечно же надо работать с CursorLoader.

Символ & перед переменной в php

#php


Что означает & перед переменной при переборе данных массива?

foreach ($this->data as $id=>&$node) {}

    


Ответы

Ответ 1



У вас есть два варианта цикла: foreach ($this->data as $id => $node) {} и (вариант со ссылкой) foreach ($this->data as $id =>& $node) {} Разница только лишь в возможности во втором случае изменить элемент массива $node простым присвоением $node = 123. Никакого замедления или копирования данных, если не используются ссылки, не происходит, поскольку PHP использует механизм Copy-on-Write. В варианте цикла без ссылки программисту просто запрещается использовать ссылку, но внутри движка PHP она все так же имеет место.

Разместить слой, после слоя с position: absolute

#html #css


Здравствуйте, вот задача: После слоя с position: absolute; нужно разместить следующий
слой, так, чтобы он стоял после предыдущего.

Пример кода:



.full-screen {
    position: absolute;
    width: 100%;
    height: 100%;
}
Цель: первый слой должен быть на весь экран, а второй должен идти после него. Эту задачу можно решить несколькими способами.


Ответы

Ответ 1



Если блок должен скроллиться html, body { height: 100%; margin: 0; } .full-screen { position: absolute; width: 100%; height: 100%; top: 0; background: silver; opacity: .5; } body:before { content: ""; display: block; height: 100%; }
Тут какой-то контент


Ответ 2



Если блок должен быть оверлеем, а НЕ скроллиться html, body, section { height: 100%; margin: 0; } .full-screen { position: absolute; width: 100%; height: 100%; top: 0; background: silver; opacity: .5; pointer-events: none; } section { overflow: auto; } section:before { content: ""; display: block; height: 100%; }
Тут какой-то контент


Различия copy функций в shutil

#python #python_3x #shutil


Есть библиотека Python3 shutil с методами:


copyfileobj
copyfile
copymode
copystat
copy
copy2
copytree
ignore_patterns


Вопросы:


В чём разница между этими методами? Кратко, если не трудно объясните в какой ситуации
каждый применяется?
Например не очень понятно как копируются метаданные без самого файла, куда именно?
И наоборот файлы отдельно от метаданных, откуда тогда метаданные берутся?
Если нужно обычное копирование файла, то что из этого использовать?

    


Ответы

Ответ 1



(1) В чём разница между этими методами? Кратко если не трудно объясните в какой ситуации каждый применяется? Если чтение документации shutil.copyfileobj()не достаточно для вас, то можно посмотреть на исходный код: def copyfileobj(fsrc, fdst, length=16*1024): """copy data from file-like object fsrc to file-like object fdst""" while 1: buf = fsrc.read(length) if not buf: break fdst.write(buf) То есть: пытаемся прочитать кусок, заданной длины, из входного файла и если что-то прочитали, то пишем это в выходной файл, иначе завершаем цикл — всё. Можно использовать для копирования содержимого между любыми файло-подобными объектами (с методами read(), write()). copyfile() принимает пути, а не сами файлы (отсюда отличия). Если на входе пути к обычным файлам, то copyfile(src, dst) сводится к простому: with open(src, 'rb') as fsrc: with open(dst, 'wb') as fdst: copyfileobj(fsrc, fdst) Открыли файлы, скопировали содержимое. (2) Например не очень понятно как копируются метаданные без самого файла, куда именно? И наоборот файлы отдельно от метаданных, откуда тогда метаданные берутся? copymode(), copystat() сводится к чтению метаданных исходного файла и применению их к выходному файлу (копирование режима доступа против копирования режима доступа + времени доступа/изменения + флаги, то есть просто chmod против utime + chmod + chflags + setxattr соответственно). Документация явно говорит, что конкретно должно быть скопировано. Если выходной файл не существует, то не к чему применять метаданные и эти функции завершатся с ошибкой (FileNotFoundError). Само содержимое файлов, кто владелец, группа, к которой файл принадлежит, не меняются этими функциями. Когда явно не копируются метаданные, то используются значения по умолчанию (к примеру, может зависеть от значения umask). (3) Если нужно обычное копирование файла, то что из этого использовать? Это зависит от того какой смысл вы вкладываете в слова "обычное копирование". copy() и copy2() это просто copyfile() вызов, за которым идёт copymode() и copystat() соответственно. Дополнительно, эти функции в отличии от copyfile() могут принимать папку куда писать. Документация явно упоминает, что copy(src, dst) это cp src dst, а copy2(src, dst) это cp -p src dst. Вторая команда больше метаданных старается скопировать: разрешения на чтение/запись/исполнение, владелец, время модификации/доступа (copy2() в отличии от cp -p не копирует владельца). copytree() рекурсивно копирует всё дерево директорий, так что тяжело её перепутать с другими функциями. copytree() по умолчанию использует copy_function=copy2 для копирования обычных файлов. ignore_patterns(*patterns) возвращает функцию, которая может быть использована в качестве значения ignore параметра при вызове copytree() (ignore(src, names) функция возвращает те имена из names=os.listdir(src), которые не должны копироваться из src директории, соответствующей текущему рекурсивному вызову). Полезно на примеры посмотреть: copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*')) source папка рекурсивно копируется в новую destination папку, игнорируя имена с расширением .pyc или начинающиеся на tmp (glob-шаблоны, как в командной строке).

Ответ 2



copyfile копирует только данные,в то время как copy копирует ещё бит разрешений. Copyfile работает быстрее copy. Copyfile () вызывает метод copyfileobj (), тогда как copy () работает путем вызова функций copyfile () и copystat (). Вызов shutil.copytree() создает новую папку с именем my_scripts_backup с тем же содержимым, что и исходная папка my_scripts. Например : import shutil, os os.chdir('C:\\') shutil.copytree('C:\\my_scripts', 'C:\\my_scripts_backup')

Как проверить e-mail c#?

#c_sharp


Можно ли отправить e-mail серверу проверочный запрос, что б подтвердить существование
ящика?  (Без авторизации). Поддерживают ли mail-сервера такую валидацию? Или максимум
что можно проверить - это существование домена, и соответствие регулярке (форме имя@домен)?
Поддерживет ли pop3 или smtp протоколы анонимную валидацию ящика?
    


Ответы

Ответ 1



Можно проверить на уровне SMTP протола: https://stackoverflow.com/questions/565504/how-to-check-if-an-email-address-exists-without-sending-an-email (под C# https://github.com/adoconnection/EmailValidator.NET) Однако, это зависит от настроек почтового сервера, и некоторые эти опции отключают, чтобы не идентифицировать своих пользователей. Поэтому, имхо, не стоит этого делать. Есть проверенный практикой метод подтвержения емейл: отправка на почту письма с ссылкой подтверждения, после которого юзер в полную меру может использовать сайт. Самый простой и явный способ.

Java хранение больших статистических данных

#java #база_данных #статистика


Здравствуйте! Необходимо хранить статистику активности очень большого количества
страниц (допустим, около 1000, для начала). Данные будут обновляться раз в 10 секунд
и должны храниться для будущего использования при отрисовке графиков. Где всё это чудо
можно хранить? Подозреваю, что SQL не очень подходит, будет слишком большая нагрузка.
    


Ответы

Ответ 1



Для начала небольшой расчет: Снимок статистики каждые 10 секунд - это 6*60*24 = 8640 срезов в сутки. Для 1000 сайтов при объеме сохраняемых данных даже в 100 байт получаем 8640*1000*100/1024/1024 = 824 Мб/сутки или 24 Гб/мес. Для записи размером в килобайт за один раз это будет соответственно четверть терабайта в месяц. Чтобы понять как эти даннные хранить, для начала нужно по такой же схеме рассчитать предполагаемый объем данных, определиться с требуемой глубиной хранения и требованиями по сохранности и доступности данных, предполагаемой интенсивностью запросов, понять насколько быстро допустимо получать отчет (графики). Можно сэкономить на старых данных увеличивая шаг агрегации для них: например, данные старше месяца объединять в получасовые интервалы, а данные старше полугода и вовсе объединять в сутки. Если речь идет о серьезном проекте, посмотрите в сторону специализированных решений для хранения временных рядов (Time Series DBMS) типа InfluxDB или событий типа яндексовского ClickHouse. Они как правило имеют функциональность для построения сложных запросов по временным рядам (сдвиги/наложения/произвольная агрегация по интервалам/дифференцирование/интегрирование и т.п.), настройки политик хранения данных разной глубины по времени, шардирование и реплицирование, кеширование "горячих" данных, но имеют и свои ограничения.

В чем отличие между HTTP методами HEAD и OPTIONS?

#веб_программирование #сеть #http #веб_сервер


В чем отличие между HTTP методами HEAD и OPTIONS? я знаю лишь что в ответ на OPTIONS
сервер должен отдать Allow со списком поддерживаемых методов. Есть ли еще какие либо
концептуальные/технические отличия?
    


Ответы

Ответ 1



У этих запросов разное назначение: HEAD - служит для проверки существования ресурса, он полностью аналогичен GET, но без возврата тела ответа OPTIONS - служит для получения параметров для ресурса или для сервера в целом и при этом сам ресурс ни как не затрагивается (то есть это более дешевая операция по сравнению с HEAD) OPTIONS возвращает параметры в заголовке. Список параметров зависит о ресурса и/или сервера. Обычно это заголовок Allow, который описывает какие методы доступны для ресурса.

Ответ 2



HEAD Данный метод по своей сути похож на GET, но сервер отвечает на запрос одним лишь заголовком. (Отсюда и название метода.) Применяется, например, чтобы узнать, существует ли в сети тот или иной URL и не произошло ли каких-нибудь изменений. OPTIONS Метод представляет запрос информации об опциях соединения, доступных в цепочке запросов/ответов, идентифицируемой запрашиваемым URI (Request-URI). Этот метод позволяет клиенту определять опции и/или требования, связанные с ресурсом, или возможностями сервера, но не производя никаких действий над ресурсом и не инициируя его загрузку.

Ответ 3



Отличие этих методов - в том, что HEAD запрашивает информацию о ресурсе, а OPTIONS запрашивает информацию о методах доступа к ресурсу. Более подробно. Заголовки, возвращаемые методом HEAD, обязаны соответствовать заголовкам, возвращаемым методом GET. При этом, метод HEAD не имеет никакого отношения к методам POST, PUT, DELETE и прочим. В то же время, настройки доступа, возвращаемые методом OPTIONS, имеют отношение сразу ко всем методом ресурса - GET, POST, PUT, DELETE и пр. Если говорить о применении, то метод HEAD может использоваться чтобы получить информацию о странице без скачивания самой страницы. Метод OPTIONS же используется, в основном, механизмом Preflight request в CORS или для обнаружения поддерживаемых сервером фич в WebDAV.

Используется ли во всех браузерах независимо от ОС перевод строки \r\n?

#linux #windows #http #браузер #apple


Добрый день! Наткнулся на информацию, что в разных операционных системах перевод
строки пишется по-разному:

Windows - \r\n

Apple - \r

Linux - \n

Но также где-то писали, что HTTP протокол независимо от системы использует \r\n.

Кому верить?
    


Ответы

Ответ 1



формат заголовков http соответствует спецификации, изложенной в rfc822. согласно секции 3.1.2 rfc822, поле заголовка должно заканчиваться двумя символами — возврат каретки (carriage-return, cr, в ascii — шестнадцатиричное значение 0d) и перевод строки (line-feed, lf, в ascii — шестнадцатиричное значение 0a): Once a field has been unfolded, it may be viewed as being composed of a field-name followed by a colon (":"), followed by a field-body, and terminated by a carriage-return/line-feed. в теле же сообщения (это может быть и html-страница, и вообще всё что угодно, например, просто бинарные данные), конечно, могут встретиться любые символы в любой комбинации.

Запустить python скрипт из php и получить результат обратно в php

#php #python


Всем привет.
Интересует такой вопрос.
Каким образом можно из php скрипта запустить python скрипт, обработать в нем некоторые
данные, и получить результат работы скрипта и данные обратно в php ?
    


Ответы

Ответ 1



В зависимости от того, что вы делаете, system () или popen (). Используйте system (), если код Python не имеет выхода, или если вы хотите, чтобы выход скрипта Python переходил непосредственно в браузер. Используйте popen (), если вы хотите записать данные на стандартный ввод скрипта Python или прочитать данные из стандартного вывода сценария Python в php. Popen () позволит вам читать или писать, но не то, и другое. Если вы хотите оба, проверьте proc_open (), но с двухсторонней связью между программами вы должны быть осторожны, чтобы избежать взаимоблокировок, где каждая программа ждет другого, чтобы что-то сделать. Если вы хотите передать данные, предоставленные пользователем, в сценарий Python, то самое важное, о чем нужно заботиться, - это командная инъекция. Если вы не будете осторожны, ваш пользователь может отправить вам такие данные, как «; evilcommand»; И заставить вашу программу выполнять произвольный код. Escapeshellarg () и escapeshellcmd () могут помочь с этим, но лично мне нравится удалять все, что не является известными хорошими данными, используя что-то вроде Preg_replace ('/ [^ a-zA-Z0-9] /', '', $ str) Источник:

Ответ 2



$result = system('python myscript.py myargs', $retval); function.system.php $python = shell_exec('C:\Python27\python.exe e:\xampp\htdocs\scripts\test.py'); function.shell-exec.php (функция недоступна в безопасном режиме)

delphi Отловить подключение usb

#delphi #usb


Нашел на просторах рунета код для отлова подключения к усб порту

 unit usb_utils;

//http://www.swissdelphicenter.ch/en/tipsindex.php

interface

uses 
  Windows, Messages, SysUtils, Classes, Forms;

type

  PDevBroadcastHdr  = ^DEV_BROADCAST_HDR; 
  DEV_BROADCAST_HDR = packed record 
    dbch_size: DWORD; 
    dbch_devicetype: DWORD; 
    dbch_reserved: DWORD; 
  end;

  PDevBroadcastDeviceInterface  = ^DEV_BROADCAST_DEVICEINTERFACE; 
  DEV_BROADCAST_DEVICEINTERFACE = record 
    dbcc_size: DWORD; 
    dbcc_devicetype: DWORD; 
    dbcc_reserved: DWORD; 
    dbcc_classguid: TGUID; 
    dbcc_name: short; 
  end;

const 
  GUID_DEVINTERFACE_USB_DEVICE: TGUID = '{A5DCBF10-6530-11D2-901F-00C04FB951ED}'; 
  DBT_DEVICEARRIVAL          = $8000;          // system detected a new device 
  DBT_DEVICEREMOVECOMPLETE   = $8004;          // device is gone 
  DBT_DEVTYP_DEVICEINTERFACE = $00000005;      // device interface class

type

  TComponentUSB = class(TComponent) 
  private 
    FWindowHandle: HWND; 
    FOnUSBArrival: TNotifyEvent; 
    FOnUSBRemove: TNotifyEvent; 
    procedure WndProc(var Msg: TMessage); 
    function USBRegister: Boolean; 
  protected 
    procedure WMDeviceChange(var Msg: TMessage); dynamic; 
  public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
  published 
    property OnUSBArrival: TNotifyEvent read FOnUSBArrival write FOnUSBArrival; 
    property OnUSBRemove: TNotifyEvent read FOnUSBRemove write FOnUSBRemove; 
  end;

implementation

constructor TComponentUSB.Create(AOwner: TComponent); 
begin 
  inherited Create(AOwner); 
  FWindowHandle := AllocateHWnd(WndProc); 
  USBRegister; 
end;

destructor TComponentUSB.Destroy; 
begin 
  DeallocateHWnd(FWindowHandle); 
  inherited Destroy; 
end;

procedure TComponentUSB.WndProc(var Msg: TMessage); 
begin 
  if (Msg.Msg = WM_DEVICECHANGE) then  
  begin 
    try 
      WMDeviceChange(Msg); 
    except 
      Application.HandleException(Self); 
    end; 
  end 
  else 
    Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam); 
end;

procedure TComponentUSB.WMDeviceChange(var Msg: TMessage); 
var 
  devType: Integer; 
  Datos: PDevBroadcastHdr; 
begin 
  if (Msg.wParam = DBT_DEVICEARRIVAL) or (Msg.wParam = DBT_DEVICEREMOVECOMPLETE) then  
  begin 
    Datos := PDevBroadcastHdr(Msg.lParam); 
    devType := Datos^.dbch_devicetype; 
    if devType = DBT_DEVTYP_DEVICEINTERFACE then  
    begin // USB Device 
      if Msg.wParam = DBT_DEVICEARRIVAL then  
      begin 
        if Assigned(FOnUSBArrival) then 
          FOnUSBArrival(Self); 
      end  
      else  
      begin 
        if Assigned(FOnUSBRemove) then 
          FOnUSBRemove(Self); 
      end; 
    end; 
  end; 
end;

function TComponentUSB.USBRegister: Boolean; 
var 
  dbi: DEV_BROADCAST_DEVICEINTERFACE; 
  Size: Integer; 
  r: Pointer; 
begin 
  Result := False; 
  Size := SizeOf(DEV_BROADCAST_DEVICEINTERFACE); 
  ZeroMemory(@dbi, Size); 
  dbi.dbcc_size := Size; 
  dbi.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE; 
  dbi.dbcc_reserved := 0; 
  dbi.dbcc_classguid  := GUID_DEVINTERFACE_USB_DEVICE; 
  dbi.dbcc_name := 0;

  r := RegisterDeviceNotification(FWindowHandle, @dbi, 
    DEVICE_NOTIFY_WINDOW_HANDLE 
    ); 
  if Assigned(r) then Result := True; 
end;

end.


Все бы хорошо, но вот не особо у меня получается его совместить с dll (хочу потом
заинжектить длл в процесс). Тобищь, нужно чтоб длл отлавливала подключения флешки,
как я использовал код выше

library dll;

uses
  SysUtils,
  Classes,windows,dialogs,messages;

type

PDevBroadcastHdr  = ^DEV_BROADCAST_HDR;
DEV_BROADCAST_HDR = packed record
   dbch_size: DWORD;
   dbch_devicetype: DWORD;
   dbch_reserved: DWORD;
end;

PDevBroadcastDeviceInterface  = ^DEV_BROADCAST_DEVICEINTERFACE;
DEV_BROADCAST_DEVICEINTERFACE = record
   dbcc_size: DWORD;
   dbcc_devicetype: DWORD;
   dbcc_reserved: DWORD;
   dbcc_classguid: TGUID;
   dbcc_name: short;
end;

const
GUID_DEVINTERFACE_USB_DEVICE: TGUID = '{A5DCBF10-6530-11D2-901F-00C04FB951ED}';
DBT_DEVICEARRIVAL          = $8000;          // system detected a new device
DBT_DEVICEREMOVECOMPLETE   = $8004;          // device is gone
DBT_DEVTYP_DEVICEINTERFACE = $00000005;      // device interface class

type

TComponentUSB = class(TComponent)
private
   FWindowHandle: HWND;
   FOnUSBArrival: TNotifyEvent;
   FOnUSBRemove: TNotifyEvent;
   procedure WndProc(var Msg: TMessage);
   function USBRegister: Boolean;
protected
   procedure WMDeviceChange(var Msg: TMessage); dynamic;
public
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
published
   property OnUSBArrival: TNotifyEvent read FOnUSBArrival write FOnUSBArrival;
   property OnUSBRemove: TNotifyEvent read FOnUSBRemove write FOnUSBRemove;
end;




{$R *.res}


constructor TComponentUSB.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FWindowHandle := AllocateHWnd(WndProc);
USBRegister;
end;

destructor TComponentUSB.Destroy;
begin
DeallocateHWnd(FWindowHandle);
inherited Destroy;
end;

procedure TComponentUSB.WndProc(var Msg: TMessage);
begin
if (Msg.Msg = WM_DEVICECHANGE) then
begin
   try
     WMDeviceChange(Msg);
   except
   showmessage('error');
  //   Application.HandleException(Self);
   end;
end
else
   Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam);
end;

procedure TComponentUSB.WMDeviceChange(var Msg: TMessage);
var
devType: Integer;
Datos: PDevBroadcastHdr;
begin
if (Msg.wParam = DBT_DEVICEARRIVAL) or (Msg.wParam = DBT_DEVICEREMOVECOMPLETE) then
begin
   Datos := PDevBroadcastHdr(Msg.lParam);
   devType := Datos^.dbch_devicetype;
   if devType = DBT_DEVTYP_DEVICEINTERFACE then
   begin // USB Device
     if Msg.wParam = DBT_DEVICEARRIVAL then
     begin
     showmessage('tada!');
       if Assigned(FOnUSBArrival) then
         FOnUSBArrival(Self);
     end
     else
     begin
       if Assigned(FOnUSBRemove) then
         FOnUSBRemove(Self);
     end;
   end;
end;
end;

function TComponentUSB.USBRegister: Boolean;
var
dbi: DEV_BROADCAST_DEVICEINTERFACE;
Size: Integer;
r: Pointer;
begin
Result := False;
Size := SizeOf(DEV_BROADCAST_DEVICEINTERFACE);
ZeroMemory(@dbi, Size);
dbi.dbcc_size := Size;
dbi.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE;
dbi.dbcc_reserved := 0;
dbi.dbcc_classguid  := GUID_DEVINTERFACE_USB_DEVICE;
dbi.dbcc_name := 0;

r := RegisterDeviceNotification(FWindowHandle, @dbi,
   DEVICE_NOTIFY_WINDOW_HANDLE
   );
if Assigned(r) then Result := True
else
showmessage('bad');

end;

begin
showmessage('good');
 TComponentUSB.Create(nil);
 while true do
 sleep(100);
end.


Инжектю длл, выходит сообщение good, значит заинжектилось норм, но вот при вставке
флэшки не выходит сообщение tada!, хотя на форме все работает хорошо. Я думаю, что
проблема в регистрации (USBRegister) мб хэндл не тот, можете мне подсказать ?
    


Ответы

Ответ 1



Представленный в вопросе код содержит ошибки: При инициализации dll (между begin и end, в секции initialization используемых модулей) крайне не рекомендуется использовать что-то "тяжелое". Объяснение легко тянет на отдельную статью (например - статья Gunsmoker-а ) Уведомления о подключении устройств к компьютеру используют штатный механизм сообщений Windows , т.е. код, реализующий этот функционал, должен выполняться в потоке, который реализует цикл выборки сообщений (Message loop). Собственно, код из вопроса не работает "благодаря" пункту 2. С учетом обоих замечаний, код должен выполнять следующие действия: Максимально быстро завершить работу между "главными" begin и end кода dll. После "отложенной" инициализации нужно организовать цикл выборки сообщений, который должен работать до момента выгрузки dll (или любых других условий, на ваш выбор). По завершению работы цикла выборки сообщений - провести деинициализацию. Пункт 1 будем выполнять при помощи потока. Т.е. при инициализации dll создаем свой поток и уже в нем проводим основную работу. Этот метод тоже не очень хорош, посему - если есть возможность, то при внедрении dll лучше вызвать отдельную функцию, экспортируемую этой dll и уже в ней производить указанные манипуляции. Итого, общий код dll будет выглядеть примерно так: var DelayedThread: TDelayedThread; procedure LibProc(Reason: integer); begin case Reason of DLL_PROCESS_ATTACH: // нашу dll загрузили begin DelayedThread:=TDelayedThread.Create; // создаем отдельный поток // внутри которого и будем осуществлять основные "тяжелые" действия DelayedThread.FreeOnTerminate:=True; // мы будем завершать поток при выгрузке dll // и из-за этого не будем ждать, пока поток завершится. Пусть он уничтожится после завершения самостоятельно. end; DLL_PROCESS_DETACH: // нашу dll выгружают begin // Завершаем поток. DelayedThread.Terminate; PostThreadMessage(DelayedThread.ThreadID, WM_QUIT, 0, 0); // отправляем нашему потоку сообщение // чтобы он "проснулся", если в данный момент он "спит" внутри GetMessage (см. код ниже). // и после пробуждения - вышел из цикла. end; end; end; begin DLLProc := LibProc; LibProc(DLL_PROCESS_ATTACH); end. Этот код выполняет действия №1 (не делать ничего "тяжелого" при инициализации dll, максимально быстро отдать управление) и №3 (деинициализация). Теперь нужно реализовать основное действие №2. Объявление класса потока оставим на ваше усмотрение, здесь же остановимся именно на реализации. Код организации выборки сообщений для работы всех окон (созданных в этом потоке) может выглядеть так: procedure TDelayedThread.Execute; var msg: TMSG; ComponentUSB: TComponentUSB; begin ComponentUSB:=TComponentUSB.Create(nil); // создание компонента, в котором есть необходимость получения сообщений // создаем именно внутри метода Execute, а не в конструкторе. // чтобы созданное внутри компонента окно принадлежало именно этому потоку. // Иначе дальнейшие действия окажутся бессмысленными. try PeekMessage(msg, 0, 0, 0, PM_NOREMOVE); // "говорим" ОС, что этот поток будет выбирать сообщения while not Terminated and GetMessage(msg, 0, 0, 0) do // и входим в цикл выборки сообщений // пока этот поток не прервут. begin TranslateMessage(msg); DispatchMessage(msg); // этот (стандартный) метод "отдает" принятое сообщение нужной оконной функции. // в данном случае - той, которая использовалась в AllocateHWND в конструкторе TComponentUSB end; finally ComponentUSB.Free; end; end; В главном потоке обычного VCL-приложения выборкой сообщений (используя подобный цикл) занимается объект Application: TApplication, а конкретнее - его метод ProcessMessages (лирическое отступление: если вам приходится принудительно вызывать этот метод у себя в приложении - скорее всего, вы что-то делаете неправильно).

Intellij Idea сворачивает директорию если в ней самой содержится только одна директория

#java #intellij_idea


Intellij Idea автоматически сворачивает директорию если в ней самой содержится только
одна директория. А как назад развернуть?


    


Ответы

Ответ 1



Чуть выше в этом навигаторе(Project) у тебя будет рисунок шестиренки. Нажми на его и выбери там Flatten Packages.

Смена курсора в WPF

#c_sharp #wpf #visual_studio


Как в WPF проекте можно изменить курсор мыши на свой, подгруженный в ресурсах проекта? 
    


Ответы

Ответ 1



Понятие «ресурса» в .NET достаточно размыто: есть linked resources, embedded resources, а также ресурсы, доступные через свойства проекта → Resources. Например, для случая linked resources (это когда вы добавляете файл в проект, и устанавливаете Build Action = Resource (не Embedded Resource!)), подойдёт следующий код для загрузки курсора: var uri = new Uri("pack://application:,,,/Res/my.cur"); var stream = Application.GetResourceStream(uri).Stream; var cursor = new Cursor(stream); в окне установите Cursor = cursor;

Обращение к элементам массива из словарей. Десериализация Json строки

#c_sharp #json #словари


В общем, дело обстоит так: нужно получать данные о карте игры с сервера. Сервер возвращает
JSON- строку, в которой есть массив из словарей. Выглядит это примерно вот так:


  {"response":{"map_width":80,"map_height":80,"map_sp":5541,"objects":[{"type":1,"health":100},{"type":1,"health":100},{"type":1,"health":100},{"type":1,"health":100},{"type":1,"health":100}]}


Я могу спокойно получить все данные, кроме objects.
Объявление массива словарей такое: public System.Collections.Generic.Dictionary[] Field_Params;.
Попытка получения данных из массива Field_Params This_Field.Field_Params[0]["type"]
безуспешна т.к. я не могу обратиться к конкретному словарю.
Помогите, пожалуйста. Либо с тем, как получить данные из конкретного словаря в массиве,
либо как лучше десериализовать возвращаемую сервером строку.
Код скрипта, который получает данные с сервера:  

using System.Net;
using Newtonsoft.Json;
using UnityEngine;
using System.Collections.Generic;

public class Web_Queries : MonoBehaviour
// Класс Веб- запросов
{
    public class Web_Field
    // Поле с данными, получаемыми с сервера
    {
        // Ответ с сервера
        [JsonProperty("response")]
        public Dictionary[] Response;
        // Ответ с сервера
        [JsonProperty("debug")]
        public string Description;
    }

    public Web_Field Get (string Url)
    // Get- запрос
    {
        // Объявляем новый экземпляр класса Web_Field
        Web_Field New_WF = new Web_Field();
        // Проверка подлинности сертефиката сайта
        ServicePointManager.ServerCertificateValidationCallback = delegate { return
true; };
        string Common_Response;
        // Создание нового объекта класса
        using (var Connection = new WebClient())
        {
            // Запрос на сайт
            Common_Response = Connection.DownloadString(Url);
        }
        New_WF = JsonConvert.DeserializeObject(Common_Response);
        // Вернуть ответ с сервера
        return New_WF;
    }
}

    


Ответы

Ответ 1



Вариант 1: Возьмем ваш JSON и прогоним его через этот сайт. В ответ получаем структуру необходимых классов для грамотной десериализации JSON. Я смотрю у вас стоит JSON.NET, поэтому используем его, десериализируем строку в наш объект: var json = JsonConvert.DeserializeObject(file);. Дальше без проблем можно получить доступ ко всему, что нам необходимо, например циклом пройтись по все данным из objects. В итоге получаем что то вроде: public class Object { public int type { get; set; } public int health { get; set; } } public class Response { public int map_width { get; set; } public int map_height { get; set; } public int map_sp { get; set; } public List objects { get; set; } } public class RootObject { public Response response { get; set; } } var file = "{\"response\":{\"map_width\":80,\"map_height\":80,\"map_sp\":5541,\"objects\":[{\"type\":1,\"health\":100},{\"type\":1,\"health\":100},{\"type\":1,\"health\":100},{\"type\":1,\"health\":100},{\"type\":1,\"health\":100}]}}"; var json = JsonConvert.DeserializeObject(file); foreach (var item in json.response.objects) { Console.WriteLine($"Type: {item.type}. Health {item.health}"); } Вариант 2 (без использования классов): Парсим строку в JObject, методом Parse. Получаем небходимые данные, путем добавления некого индекса к нашему JObject. Сам код: var file = "{\"response\":{\"map_width\":80,\"map_height\":80,\"map_sp\":5541,\"objects\":[{\"type\":1,\"health\":100},{\"type\":1,\"health\":100},{\"type\":1,\"health\":100},{\"type\":1,\"health\":100},{\"type\":1,\"health\":100}]}}"; var json = JObject.Parse(file); foreach (var item in json["response"]["objects"]) { Console.WriteLine($"Type: {item["type"]}. Health {item["health"]}"); } Второй вариант хорошо подойдет маленькой утилите, которая использует небольшие данные JSON, на большом проекте его будет трудно изменять/масштабировать под сервер, ибо изменится значение на сервере - придется все строки переписывать и во всем коде. В первом варианте у вас есть структура, классы, которые соответствую тому, что есть на сервере в формате JSON, изменится у сервера что либо - быстро подправили класс и в нужном месте исправили ошибки. Что выбирать - решать вам.

Повтор обработки событий в Java

#java #javafx


Есть кнопка, при нажатии на которую проигрывается звук и меняется цвет. Как заставить
через время возвращать кнопку первоначальный цвет и добавить возможность много раз
нажимать на кнопку? 

    Number6.setOnAction(event -> {
        PSTriple.play(); // PSTriple - медиафайл
        Number6.setStyle("-fx-background-color: red");
        Number6.
    });

    


Ответы

Ответ 1



Запустите таймер, по истечении которого верните кнопку в нужное состояние: private static void returnButtonState(final Button button, long delay) { Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { //логика с кнопкой timer.cancel(); } }, delay, 1000); } Пример вызова: Number6.setOnAction(event -> { PSTriple.play(); // PSTriple - медиафайл Number6.setStyle("-fx-background-color: red"); returnButtonState(Number6, 3*1000); //вызовется 1 раз через 3 сек

Ответ 2



Я вот так делал. С использованием Thread В обработчик события добавляем строчку: laterRepaint(300); Далее сама функция: private void laterRepaint(int delay) { Thread thread = new Thread(() - { try { Thread.sleep(delay); } catch (InterruptedException e1) { e1.printStackTrace(); } ... далее идут действия с кнопкой ... }); thread.start(); }

Сравнение двух списков состоящих из массивов

#c_sharp #массивы #список #сравнение


Есть два списка List list1 и list2. Элементы в них могут совпадать. 
Мне нужно получить новый список list3 в который войдут элементы из list2, которых
не было в list1.

Я пытаюсь реализовать это так, но условие не работает и на выходе список пустой.

List list3 = list2.Where(x => !list1.Contains(x)).ToList();


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

for (byte i = 0; i < list2.Count; ++i)
    for (byte j = 0; j < list1.Count; ++j)
        {
            if (list2[i].SequenceEqual(list1[j]))
                list3.Add(list2[i]);
        }

    


Ответы

Ответ 1



Первый вариант не работал потому, что Contains сравнивает через Equals, а равенство по Equals для массивов есть равенство ссылок. А вам ведь нужно SequenceEqual вместо этого. Поэтому можно написать свой вариант Contains: x => !list1.Any(y => y.SequenceEqual(x))

listbox переместить выделение на последний элемент

#c_sharp #wpf


Необходимо сделать пролистывание ListBox в самый низ. Делаю при инициализации окна: 

lbMsg.SelectedItem = lbMsg.Items.Count - 1;


Но ничего не происходит.
    


Ответы

Ответ 1



Попробуйте так: lbMsb.ScrollIntoView(lbMsg.Items[lbMsg.Items.Count - 1]);

Упаковка содержимого папки в tar.gz архив

#linux #tar


Как создать архив из содержимого папки (т.е. ее самой в архиве быть не должно)?
    


Ответы

Ответ 1



Зайти в папку и выполнить tar -czf ../имя_архива.tar.gz . Работает и со скрытыми файлами

Ответ 2



Зайти в папку и создать архив из всего, что в ней есть: tar -cf result.tar *

Когда правильно закрывать файл и снимать блокировку?

#php


Приветствую. Мне нужно сделать следующее:


Прочитать содержимое файла.
Удалить файл.


При этом файл не должен быть доступен для записи до тех пор, пока мы не закончим
чтение и не удалим файл.

Вот фрагмент кода:

$h = fopen(__DIR__.'/file.txt', 'r+');
flock($h, LOCK_EX);
$str = file_get_contents(__DIR__.'/file.txt');
unlink(__DIR__.'/test.txt');
flock($h, LOCK_UN);
fclose($h);


Правильно ли так писать? Просто закрывать и разблокировать файл, которого уже нет
выглядит не очень корректно... А если удалять уже после разблокировки и вызова fclose(),
то в промежутке между fclose() и unlink() другой скрипт ведь может что-то записать в файл.
    


Ответы

Ответ 1



Просто закрывать и разблокировать файл, которого уже нет А он ещё есть в действительности. И будет доступен его открывшим приложениям всё время, пока на него есть открытые ссылки. И только когда закроется последний открытый дескриптор файл пропадёт (и то пропадёт только когда ФС скомандует trim либо переиспользует блоки под что-нибудь другое). melkij@melkij:~$ echo 42 > tmp/rmfile melkij@melkij:~$ php -a Interactive mode enabled php > $fp = fopen('tmp/rmfile', 'r+'); php > unlink('tmp/rmfile'); php > echo fread($fp, 100); 42 php > var_dump(file_exists('tmp/rmfile')); bool(false) Эта особенность, кстати, иногда может помочь восстановить случайно удалённый файл чтением открытого дескриптора из /proc/$PID/fd

Ответ 2



Есть еще из книги PHP5 2-е издание (Котерова):

Заморозить часть строки в git'е

#git #npm #gitignore


Есть npm'овский packadge.json, один из модулей в котором подключается как git-репозиторий
с тегом. Проблема в том, что адрес репозитория должен содержать имя пользователя:

"some-module": "git+https://qwertiy@git.smth.com/some-module.git#1.2.3"
                            ^^^^^^^_______________________________________ у каждого своё
                                                                 ^^^^^____ версия
меняется


Можно ли как-то настроить git, чтобы он при выполнении pull, commit, rebase и других
команд игнорировал имя пользователя, но обновлял версию пакета без merge-конфликтов?

Т. е. везде имя должно оставаться без изменений: локально - локальное имя, а в закоммиченном
- закоммиченное.

А может быть есть какие-то другие способы, связанные не с git'ом, а с npm'ом?
    


Ответы

Ответ 1



Да, это можно настроить в git. Называется smudge /clean (загрязнить-почистить). Суть в том, что при добавлении файла или извлечении с репозитория он будет пропускаться через фильтр. Итого, нужно будет два фильтра - один будет подставлять правильного пользователя, второй - заменять его на некого "универсально", которого будем оставлять в коде. Здесь расписано детально, как это работает https://alexzaytsev.me/2014/10/19/git-filter-to-convert-spaces-to-tabs-and-vice-versa/ - но тут для пробелов. Вначале в .gitconfig (или .git\info\attributes) добавить фильтр package.json filter=hide_my_name и добавить собственно сами фильтра в gitconfig [filter "hide_my_name"] clean = 'remove-name' smudge = 'restore-name' remove-name и restore-name - это две программы (или скрипта), которые на вход получат содержимое файла на стандартный ввод и должны будут сделать замену. clean задача будет запускаться перед git add, а smugle - после checkout. Написание самых скриптов и как хранить имя для конкретного пользователя - это уже домашняя задача. Но я не исключаю, что эта задача может иметь такое решение: [filter "hide_my_name"] clean = 'perl -pe "s!git+https://qwertiy@!git+https://XXXXXXX@!"' smudge = 'perl -pe "s!git+https://XXXXXXX@!git+https://qwertiy@!"'

Преобразовать строку Pandas в словарь

#python #pandas #dataframe


Добрый день.

У меня есть датафрейм пандаса с составным индексом. Мне нужно перебрать все строки
и каждую превратить в словарь вида {название_колонки: значение}.

Причем, чтобы туда попадали и индексные колонки, и простые.

Всё утро читаю документацию, написал вот такое:

for row in df.itertuples():
  idx_dct = dict(zip(df.index.names, row[0]))
  val_dct = dict(zip(df.columns, row[1:]))
  res_dct = dict(idx_dct, **val_dct)
  print(res_dct)


Результат, конечно, получен. Но код мне совершенно не нравится и есть такое ощущение,
что я изобретаю велосипед.

Может быть, в пандасе есть какие-то специальные методы для того, что я хочу?

UPD: Исходные датафрейм получен как pivot_table от другого датафрейма и имеет вид:

           val
idx1 idx2     
1    1       1
     2       8
3    3       2


Я хочу получить список словарей:

{'idx2': 1, 'idx1': 1, 'val': 1}
{'idx2': 2, 'idx1': 1, 'val': 8}
{'idx2': 3, 'idx1': 3, 'val': 2}


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


Ответы

Ответ 1



UPDATE: с использованием DF приведенном в измененном вопросе: In [24]: df Out[24]: val idx1 idx2 1 1 1 2 8 3 3 2 In [25]: df.reset_index().to_dict('records') Out[25]: [{'idx1': 1, 'idx2': 1, 'val': 1}, {'idx1': 1, 'idx2': 2, 'val': 8}, {'idx1': 3, 'idx2': 3, 'val': 2}] Это делается очень просто (если я правильно понял вопрос): Пример DF: In [15]: df Out[15]: a b c d first second bar one 1.009758 -0.384033 1.668898 -1.302448 two -0.356388 0.341411 0.109125 -0.202563 baz one 0.495853 1.052266 -0.264984 -0.343306 two -1.571577 -0.646954 -0.187620 0.594999 foo one -1.228468 0.092976 0.030144 -2.099977 two 1.588960 1.000785 0.875712 2.584941 qux one 0.590220 -1.305587 1.270706 -0.351706 two -0.643912 -0.597870 -0.921415 1.034810 Решение - получаем список словарей: In [16]: df.reset_index().to_dict('records') Out[16]: [{'a': 1.0097583468728302, 'b': -0.3840332867221293, 'c': 1.6688984434963192, 'd': -1.3024483316392064, 'first': 'bar', 'second': 'one'}, {'a': -0.3563883540306324, 'b': 0.3414110282364142, 'c': 0.10912532819005455, 'd': -0.20256250429340558, 'first': 'bar', 'second': 'two'}, {'a': 0.4958529642501866, 'b': 1.0522662314224476, 'c': -0.26498405219799387, 'd': -0.3433064145597633, 'first': 'baz', 'second': 'one'}, {'a': -1.5715767690652416, 'b': -0.6469535251089127, 'c': -0.18762036338991817, 'd': 0.5949991666517178, 'first': 'baz', 'second': 'two'}, {'a': -1.2284678785281338, 'b': 0.09297646032741932, 'c': 0.030143674588866923, 'd': -2.0999765379840656, 'first': 'foo', 'second': 'one'}, {'a': 1.5889599693307292, 'b': 1.0007848283996947, 'c': 0.8757119900619003, 'd': 2.5849411147424046, 'first': 'foo', 'second': 'two'}, {'a': 0.5902198338135144, 'b': -1.305587223739773, 'c': 1.270706125958048, 'd': -0.3517064424514258, 'first': 'qux', 'second': 'one'}, {'a': -0.6439118906436252, 'b': -0.5978697429631142, 'c': -0.9214146623018005, 'd': 1.034810389524401, 'first': 'qux', 'second': 'two'}]

Не удается конвертировать String в enum

#java #enum


public enum Colors {

    WHITE("white"), LIGHT_GRAY("lightGray"), GRAY("gray"), 
    DARK_GRAY("darkGray"), BLACK("black"), RED("red"), PINK("pink"), 
    ORANGE("orange"), YELLOW("yellow"), GREEN("green"), MAGENTA("magenta"), 
    CYAN("cyan"), BLUE("blue");

    private final String name;

    private Colors(String _name) {
        name = Colors.fromString(_name);//здесь компилятор ругается incompatible types
    }

    public String getName() {
        return name;
    }

    public static Colors fromString(String _name) {
        if (_name != null) {
            for (Colors clr : Colors.values()) {
                if (_name.equalsIgnoreCase(clr.name)) {
                    return clr;
                }
            }
        }
        throw new IllegalArgumentException("No such value");
    }

}

    


Ответы

Ответ 1



Вы неправильно использовали конструктор для Enum. Кроме того, обычно Enum именуется существительным в единцественном числе. public static void main(String[] args) throws Exception { System.out.println(Color.fromString("black")); System.out.println(Color.fromString("BlaCk")); System.out.println(Color.fromString("qqq")); } public enum Color { WHITE("white"), LIGHT_GRAY("lightGray"), GRAY("gray"), DARK_GRAY("darkGray"), BLACK("black"), RED("red"), PINK("pink"), ORANGE("orange"), YELLOW("yellow"), GREEN("green"), MAGENTA("magenta"), CYAN("cyan"), BLUE("blue"); private final String name; Color(String name) { this.name = name; } public String getName() { return name; } public static Color fromString(String name) { for (Color color : Color.values()) { if (color.name.equalsIgnoreCase(name)) { return color; } } throw new IllegalArgumentException("No such value '" + name + "'"); } } Результат: BLACK BLACK Exception in thread "main" java.lang.IllegalArgumentException: No such value 'qqq'

Почему ready() всегда выдает true?

#java #циклы


Имеется такой код:

public static void main(String[] args) throws IOException {
      StringReader reader = new StringReader("Khoor Dpljr");
      System.out.println(decode(reader, -3));  //Hello Amigo
}

public static String decode(StringReader reader, int key) throws IOException {
      BufferedReader br = new BufferedReader(reader);
      while (br.ready())
      {
            int b = br.read();
            b = b + key;
            char c = (char) b;
            System.out.println(c);
      }
      return null;
}


Почему ready() всегда выдает true?
    


Ответы

Ответ 1



Потому что так написано в документации к этому методу: True if the next read() is guaranteed not to block for input, false otherwise Перевод: true, если гарантируется, что следующий вызов метода read() будет неблокирующим Так как при достижении конца потока метод read сразу возвращает -1, то и ready() возвращает true. Чтобы код заработал, можно переписать его так: int c; while ((c = bufferedReader.read()) != -1) { // ... } Частичный перевод ответа @StephenC

Как записать в файл, который хранится в ресурсах?

#java #io #resources


Здравствуйте!
Подскажите пожалуйста, у меня есть текстовый файл, где хранятся очки игры, предполагал,
что файл будет хранится в ресурсах, с чтением файла я разобрался, но мне надо в него
и записывать, новый рекорд например. Как это сделать? Или подскажите как лучше поступить
в данной ситуации.
    


Ответы

Ответ 1



Путь тот же самый что и при считывании. Пример записи: public class MyClass { public static void main(String[] args) throws IllegalAccessException, IOException { String fileName = "someFile.txt"; ClassLoader classLoader = MyClass.class.getClassLoader(); Path path = Paths.get(classLoader.getResource(fileName).getPath()); writeFile(path, "test data"); } private static void writeFile(Path path, String data) throws IOException { Files.write(path, data.getBytes(), StandardOpenOption.WRITE); } } Опции записи задаются с помощью StandardOpenOption констант путем перечисления (например создать новый файл, или дописать в конец файла) Самый простой способ в Вашем случае: при старте программы считывать данные и преобразовывать в какой-нибудь класс, а затем при завершении работы перезаписывать файлик данными из этого класса.

Доступ к ListView из другого потока

#java #android #многопоточность #android_studio


Запускаю метод загрузки данных из БД из потока 

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
    getAvailableRecipes();
    }
});
thread.start();


В данном методе происходит присвоение адаптера ListView, но в итоге получаю ошибку,
что сделать это можно из UI-потока. Каким образом я могу получить доступ к ListView
и другого потока? В C# эта проблема решалась с помощью делегатов, а как обстоят дела
в java? Воспроизвести по аналогии вряд ли смогу, поскольку с java на "вы". Есть ли
какие-то отработанные методы решения данной проблемы?
    


Ответы

Ответ 1



Разделите этот метод на два: загрузка данных (долгая операция) обновление UI (в вашем случае это присвоение адаптера) Вторую часть нужно выполнять в UI потоке, можно, например, воспользоваться методом runOnUiThread (русская статья про этот метод): void getAvailableRecipes() { ... загрузка данных ... activity.runOnUiThread(new Runnable() { ... обновление UI ... }); } Так как такой паттерн: Запустить долгую задачу в фоновом потоке Изменить что-то в UI на основе результатов задачи часто встречается в андроид-приложениях, то разработчики придумали специальный класс AsyncTask, значительно упрощающий жизнь. Вот русская статья про него. Код с ним выглядит примерно так: new AsyncTask { protected Result doInBackground(Params... params) { ... долгая фоновая операция ... } protected void onProgressUpdate(Progress... progress) { ... обновление прогресса (можно оставить метод пустым) ... } protected void onPostExecute(Result result) { ... обновление UI ... } }().execute(param1, param2, ...) Некоторые пояснения: Params — класс параметров, которые вы будете передавать в execute. Можно не передавать никаких параметров и использовать Void Progress — класс, на основе которого будет обновляться прогресс (например, если AsyncTask скачивает файл, то это может быть Integer, представляющий процент скачанного). Можно никак не отображать прогресс и использовать Void Result — класс, который возвращает метод doInBackground и который принимает метод onPostExecute

Практическая разница между подходами к наследованию в Entity Framework при разработке

#c_sharp #sql #entity_framework


Здравствуйте ,изучая материалы по Entity Framework прочитал о возможностях и разных
подходах при реализации наследования в данном фреймворке . И меня конечно же заинтересовал
такой вопрос : 

Какая разница между их использованием и реализацией при разработке?

Какие же пункты я буду освещать при формировании ответа  :


Теория - здесь я буду рассказывать какие собственно есть подходы и как они отличаются
в плане реализации 
Практика - здесь я буду показывать саму реализацию кода модели классов + покажу как
будет выглядеть таблица/таблицы баз данных при разных подходах
Характеристическое сравнение - в заключительном участке мы с вами выясним какой же
подход ,в какой категории и почему именно этому подходу дано предпочтение .


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


Ответы

Ответ 1



Итак ,начнем пожалуй . Теоретическая часть : Всего в Entity Framework есть всего 3 подхода : TPH (Table Per Hierarchy - Таблица на одну иерархию классов) TPT (Table Per Type - Таблица на тип) TPC (Table Per Class(Concrete Type) - Таблица на каждый отдельный тип/класс) 1) TPH (Table Per Hierarchy - Таблица на одну иерархию классов) - При использовании данного подхода - для одной иерархии классов используется одна таблица. Данные базовых и производных классов сохраняются в одну таблицу, а для их отличия создается специальный столбец. 2) TPT (Table Per Type - Таблица на тип) - данный подход предполагает сохранение в общей таблице только тех свойств, которые общие для всех классом-наследников, то есть которые определены в базовом классе. А те свойства, которые относятся только к производному классу, сохраняются в отдельной таблице. 3) TPC (Table Per Class(Concrete Type) - Таблица на каждый отдельный тип/класс) - предполагает создание для каждой модели по отдельной таблицы. Столбцы в каждой таблице создаются по всем свойствам, в том числе и унаследованным. Здесь мы с теоретической частью пожалуй закончим - перейдем до практики: Практическая часть: Я считаю ,что надо выяснить одну деталь сразу : Суть и предназначение у этих подходов одна - отобразить зависимость класса-наследника от класса-родителя и разность заключается в том ,что я буду показывать какой именно подход будет оптимальней с разных характеристических сторон(ну и само собою - отличия при их практической реализации). Спасибо за внимание! 1) Подход TPH : Итак у нас есть базовая мини-иерархия двух классов : public class Phone { public int Id { get; set; } public string Name { get; set; } public string Company { get; set; } public int Price { get; set; } } public class Smartphone : Phone { public string OS { get; set; } } class MobileContext : DbContext { public MobileContext() : base("DefaultConnection") { } public DbSet Phones { get; set; } public DbSet Smarts { get; set; } } Здесь класс Smartphone наследуется от Phone, определяя одно свойство в дополнение к унаследованным. И при работе будет создана такая таблица: Кроме всех свойств классов Phone и Smartphone здесь также появляется еще один столбец - Discriminator. Он имеет тип nvarchar и имеет длину в 128 символов. Данный столбец и будет определять относится строка к типу Phone или Smartphone. Работа в программе : using(MobileContext db = new MobileContext()) { db.Phones.Add(new Phone {Name = "Samsung Galaxy S5", Company = "Samsung", Price = 14000 }); db.Phones.Add(new Phone {Name = "Nokia Lumia 630", Company = "Nokia", Price = 8000 }); Smartphone s1 = new Smartphone { Name = "iPhone 6", Company = "Apple", Price = 32000, OS = "iOS" }; db.Smarts.Add(s1); db.SaveChanges(); foreach (Phone p in db.Phones) Console.WriteLine("{0} ({1}) - {2}", p.Name, p.Company, p.Price); Console.WriteLine(); foreach (Smartphone p in db.Smarts) Console.WriteLine("{0} ({1}, {2}) - {3}", p.Name, p.Company, p.Price, p.OS); } Прошу вас обратить внимание на одну деталь : при выводе данных из Phones также будет идти вывод экземпляра Smartphone вместе с остальными экземплярами ,поскольку здесь SmartPhone и есть объектом Phone. 2) Подход TPT : Чтобы применить подход, возьмем из предыдущего примера систему классов и добавим к классу Smartphone атрибут [Table]: public class Phone { public int Id { get; set; } public string Name { get; set; } public string Company { get; set; } public int Price { get; set; } } [Table("Smartphones")] public class Smartphone : Phone { public string OS { get; set; } } class MobileContext : DbContext { public MobileContext() : base("DefaultConnection") { } public DbSet Phones { get; set; } public DbSet Smarts { get; set; } } Все остальное остается также, как и при подходе TPH. Но теперь база данных будет содержать следующие таблицы: Таблица для смартфонов содержит только одно поле OS, а также ключ Id для связи с таблицей Phones. Применение моделей будет аналогично подходу TPH(пункт выше): using(MobileContext db = new MobileContext()) { db.Phones.Add(new Phone {Name = "Samsung Galaxy S5", Company = "Samsung", Price = 14000 }); db.Phones.Add(new Phone {Name = "Nokia Lumia 630", Company = "Nokia", Price = 8000 }); Smartphone s1 = new Smartphone { Name = "iPhone 6", Company = "Apple", Price = 32000, OS = "iOS" }; db.Smarts.Add(s1); db.SaveChanges(); foreach (Phone p in db.Phones) Console.WriteLine("{0} ({1}) - {2}", p.Name, p.Company, p.Price); Console.WriteLine(); foreach (Smartphone p in db.Smarts) Console.WriteLine("{0} ({1}, {2}) - {3}", p.Name, p.Company, p.Price, p.OS); } 3) Подход TPC : Чтобы применить подход, изменим объявления моделей и контекст следующим образом: public class Phone { [Key, DatabaseGenerated (DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } public string Name { get; set; } public string Company { get; set; } public int Price { get; set; } } public class Smartphone : Phone { public string OS { get; set; } } class MobileContext : DbContext { public MobileContext() : base("DefaultConnection") { } public DbSet Phones { get; set; } public DbSet Smarts { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity() .Map(m => { m.MapInheritedProperties(); m.ToTable("Phones"); }); modelBuilder.Entity().Map(m => { m.MapInheritedProperties(); m.ToTable("Smarts"); }); } } Во-первых, обратите внимание, что у класса Phone в качестве типа ключа используется не int, а Guid. Это поможет нам избежать некоторых проблем с ключами. Хотя также можно было бы использовать int с ручной установкой Id при создании объекта. Во-вторых, при настройке сопоставления моделей и таблиц у каждой модели вызывается метод MapInheritedProperties(), который указывает Entity Framework-у, что в таблицу для данной модели надо включить также наследуемые свойства, а не только те, которые определены непосредственно в этой модели. При генерации базы данных у нас будут созданы две таблицы с полным набором столбцов: Применение моделей: using(MobileContext db = new MobileContext()) { db.Phones.Add(new Phone {Name = "Samsung Galaxy S5", Company = "Samsung", Price = 14000 }); db.Phones.Add(new Phone {Name = "Nokia Lumia 630", Company = "Nokia", Price = 8000 }); Smartphone s1 = new Smartphone { Name = "iPhone 6", Company = "Apple", Price = 32000, OS = "iOS" }; db.Smarts.Add(s1); db.SaveChanges(); foreach (Phone p in db.Phones) Console.WriteLine("{0} ({1}) - {2}", p.Name, p.Company, p.Price); Console.WriteLine(); foreach (Smartphone p in db.Smarts) Console.WriteLine("{0} ({1}, {2}) - {3}", p.Name, p.Company, p.Price, p.OS); } Несмотря на то, что объект Smartphone никак не связан с таблицей Phones, при извлечении данных он также будет находится в наборе db.Phones, потому что наследование все равно будет действовать. Переходим к третьей части - Характеристическое сравнение : Вот тут уже действительно интересно: В зависимости от ваших требований и требований заказчика - здесь попросту нет 'лучшего' решения ... Но: Движок Entity Framework хоть и поддерживает подход TPC , но для его адекватной работы нам нужно метод OnModelCreating() переопределять , дабы фреймворк понял, что мы подвязываем два класса между собою наследственными связями - чтобы можно было нормально работать с результатом запросов используя этот подход в приложении . Значит ли это то ,что в большинстве случаев нам будет придеться использовать 2-а первых подхода - TPH и TPT ,чтобы не тратить время на изощрения с TPC и относительную скорость работы приложения при получении запросов? Давайте выясним! Итак ,здесь мы использовать формат "критерий - условный победитель - почему" : Скорость исполнения(работы) - TPH -> Таблица на одну иерархию классов в общем имеет лучшую скорость хотя бы потому ,что на не надо делать запросы JOIN поскольку все данные в одной таблице . Такое решение становиться даже более очевидным ,когда у нас наследственная иерархия делается "шире" и "глубже". Гибкость - TPT -> Таблица на тип более гибкая потому,что решает проблему редактирования и обновлений колонок дочерней-таблицы при этом не изменяя родительскую таблицу. Эстетичность - TPT -> это уже чисто субъективное мнение ,но как по-мне ,то TPT выглядит для мене более объектно-ориентированным подходом. Использование памяти - TPT -> если у вас иерархия наследования имеет очень много различных типов , то использование TPT позволит использовать данные ,которые имеют много незаполненных полей , тем более ,если структура баз данных решает проблему множества пустых полей ,то эта проблема вряд ли скажется на производительности работы запросов. Как мы с вами можем заметить ,если вы или ваш заказчик знает на какой критерий делать упор - то вы можете сделать очевидный выбор исходя из информации выше Единственный нюанс состоит в том ,что в 90% случаев ставка идет на Производительность,поэтому в большинстве случаев использование TPH(Таблица на одну иерархию) для оптимизации работы запросов - будет оптимальней Надеюсь ,я этой статьей смог внести немного ясности в вопрос - если у вас есть пожелания ,то комментарии всегда открыты для вас - Спасибо за внимание,комрады! Само собой ссылки на источники : практические примеры и использование данных подходов в приложениях + отличительные характеристики каждого из подходов