Страницы

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

четверг, 25 апреля 2019 г.

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) мб хэндл не тот, можете мне подсказать ?


Ответ

Представленный в вопросе код содержит ошибки:
При инициализации 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 (лирическое отступление: если вам приходится принудительно вызывать этот метод у себя в приложении - скорее всего, вы что-то делаете неправильно).

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

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


Ответ

Понятие «ресурса» в .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;

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

Есть два списка 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]); }


Ответ

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

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

Необходимо сделать пролистывание ListBox в самый низ. Делаю при инициализации окна:
lbMsg.SelectedItem = lbMsg.Items.Count - 1;
Но ничего не происходит.


Ответ

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

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

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


Ответ

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

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

Приветствую. Мне нужно сделать следующее:
Прочитать содержимое файла. Удалить файл.
При этом файл не должен быть доступен для записи до тех пор, пока мы не закончим чтение и не удалим файл.
Вот фрагмент кода:
$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() другой скрипт ведь может что-то записать в файл.


Ответ

Просто закрывать и разблокировать файл, которого уже нет
А он ещё есть в действительности. И будет доступен его открывшим приложениям всё время, пока на него есть открытые ссылки. И только когда закроется последний открытый дескриптор файл пропадёт (и то пропадёт только когда ФС скомандует 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

Как работают индексированные представления?

После добавления индекса они "материализуются" ?
Если да, то каким образом происходит обновление представления? Например, запрос начал возвращать дополнительные строки. Они пересоздаются по какому-то расписанию?
Имеет ли разницу использовать индексированые представления на часто меняющихся данных или данных, которые редко изменяются?


Ответ

После добавления индекса они "материализуются" ?
Именно так. Добавление уникального кластерного индекса материализует данные, возвращаемые представлением, в этот самый кластерный индекс (т.е., по существу, в отдельную таблицу).
Если да, то каким образом происходит обновление представления? Например, запрос начал возвращать дополнительные строки. Они пересоздаются по какому-то расписанию?
Обновление материализованного представления происходит синхронно с изменением данных в таблицах, на которых оно основано, и инкрементально (индексы представлений не перестраиваются полностью каждый раз при изменении данных, а дополняются или модифицируются).
Представьте, что имеется обычная таблица с дополнительным индексом. Если добавить строк в таблицу, то индекс обновится в соответствии с данными таблицы. Механизм обновления данных материализованного представления аналогичен механизму обновления данных в индексе из данных таблицы.
Поэтому
Имеет ли разницу использовать индексированные представления на часто меняющихся данных или данных, которые редко изменяются?
да, использование индексированных представлений на часто меняющихся данных, как правило, менее желательно, чем на редко меняющихся, т.к. наличие индексированного представления будет замедлять изменение этих данных.
Достаточно большое число специальных требований и ограничений, которым должно удовлетворять представление, чтобы его можно было материализовать (привязка к схеме, требования детерминированности, невозможность использования неаддитивных агрегирующих функций), связано именно с реализацией материализованных представлений в виде индекса и инкрементальностью обновления данных в них.

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

Имеется такой код:
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?


Ответ

Потому что так написано в документации к этому методу
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

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

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


Ответ

Путь тот же самый что и при считывании. Пример записи:
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 констант путем перечисления (например создать новый файл, или дописать в конец файла) Самый простой способ в Вашем случае: при старте программы считывать данные и преобразовывать в какой-нибудь класс, а затем при завершении работы перезаписывать файлик данными из этого класса.

Разбить строку на N символов (подстрок одинаковой длины) и занести в массив

Имеется строка из X символов, необходимо разбить её на N символов и забить значения по переменным, если в конечном значении отсеченная часть меньше, чем N, то мы её удаляем. На примере: Имеется строка London_is_the_capital_of_great_Britain. Её необходимо разбить на группы по 6 символов и каждую группу внести в отдельную строчную переменную (или в массив).
s='London_is_the_capital_of_great_Britain' [s[x:x+6] for x in range (0, len(s), 6)] ['London', '_is_th', 'e_capi', 'tal_of', '_great', '_Brita', 'in']
Здесь видно, что последняя группа содержит менее 6 символов, значит мы ее не должны заносить в отдельную переменную, а просто отсечь.


Ответ

В itertools-recipes приведен пример реализации функции grouper, которая принимает на вход итерируемый объект и число, обозначающее размер последовательностей, на которые будет разбит исходный объект. В основе функции лежит zip_longest - может быть заменена на встроенный zip (комментарий jfs).
def grouper(iterable, n): args = [iter(iterable)] * n return zip(*args)
s = 'London_is_the_capital_of_great_Britain' l = [''.join(i) for i in grouper(s, 6)] print(l)
['London', '_is_th', 'e_capi', 'tal_of', '_great', '_Brita']

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

Запускаю метод загрузки данных из БД из потока
Thread thread = new Thread(new Runnable() { @Override public void run() { getAvailableRecipes(); } }); thread.start();
В данном методе происходит присвоение адаптера ListView, но в итоге получаю ошибку, что сделать это можно из UI-потока. Каким образом я могу получить доступ к ListView и другого потока? В C# эта проблема решалась с помощью делегатов, а как обстоят дела в java? Воспроизвести по аналогии вряд ли смогу, поскольку с java на "вы". Есть ли какие-то отработанные методы решения данной проблемы?


Ответ

Разделите этот метод на два:
загрузка данных (долгая операция) обновление 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 при разработке

Здравствуйте ,изучая материалы по Entity Framework прочитал о возможностях и разных подходах при реализации наследования в данном фреймворке . И меня конечно же заинтересовал такой вопрос :
Какая разница между их использованием и реализацией при разработке?
Какие же пункты я буду освещать при формировании ответа :
Теория - здесь я буду рассказывать какие собственно есть подходы и как они отличаются в плане реализации Практика - здесь я буду показывать саму реализацию кода модели классов + покажу как будет выглядеть таблица/таблицы баз данных при разных подходах Характеристическое сравнение - в заключительном участке мы с вами выясним какой же подход ,в какой категории и почему именно этому подходу дано предпочтение .
Значение данной статьи : пока изучал ,собственно, эти же подходы ,то в голову сразу пришла идея создать пособие где каждый может наглядно увидеть разницу при их использовании и на основе характеристик и практических примеров увидеть и решить для себя какой именно подход будет оптимальным


Ответ

Итак ,начнем пожалуй .
Теоретическая часть :
Всего в 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(Таблица на одну иерархию) для оптимизации работы запросов - будет оптимальней
Надеюсь ,я этой статьей смог внести немного ясности в вопрос - если у вас есть пожелания ,то комментарии всегда открыты для вас - Спасибо за внимание,комрады!
Само собой ссылки на источники : практические примеры и использование данных подходов в приложениях + отличительные характеристики каждого из подходов

Как лайкнуть репозиторий на github через requests.post?


Или может есть библиотека с такой возможностью ?


Ответ

Звёздочку репозиторию можно поставить с помощью GitHub REST API v3
PUT /user/starred/:owner/:repo
Есть много библиотек на Питоне, которые оборачивают это API. Легко свою создать:
import requests
class Github: def __init__(self, token, api_url='https://api.github.com'): self.token = token self.api_url = api_url self.headers = { 'Accept': 'application/vnd.github.v3+json', 'Authorization': 'token ' + self.token }
def star(self, repo, owner): requests.put(f"{self.api_url}/user/starred/{owner}/{repo}", headers=self.headers).raise_for_status()
Пример, чтобы звёздочку cpython поставить:
import os
github = Github(os.environ['GITHUB_OAUTH_TOKEN']) github.star(repo='cpython', owner='python')
Чтобы попробовать, можно завести личный token. Для приложений, можно автоматически token создать

WPF: Самый простой способ вызывать метод сочетанием клавиш

Я просмотрел много вопросов и ответов на англоязычном Stack Overflow, но так и не нашел ответа на вопрос: как вызывать какой-либо метод с помощью сочетания клавиш в WPF-приложениях? В Windows Forms Application это делалось довольно просто, но похоже в более развитом WPF это гораздо сложнее.
Ниже представлен классический пример. Нигде, где я смотел, толком не объяснено, что именно мы привязываем. Но это явно не метод. Что это вообще за ApplicationCommands?

В другом вопросе было сказано: "XAML - это язык разметки, а потому мы не можем так просто вызвать метод оттуда". Хорошо, почему мы тогда с лёгкостью можем привязать метод к кнопке?