Страницы

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

Показаны сообщения с ярлыком desktop. Показать все сообщения
Показаны сообщения с ярлыком desktop. Показать все сообщения

воскресенье, 15 марта 2020 г.

Создание ПО с плагинами

#c_sharp #delphi #plugin #desktop #рефлексия


По долгу службы требуется реализация довольно сложного десктопного ПО. Как одно из
требований — плагины (подключаемые модули), которые могут быть очень разнообразными:
от определения новых типов данных до работы с интерфейсом (добавление форм, кнопок
и т.п.). Разумеется, также требуются аналитика, обработка данных, сохранение в разные
БД и т.д.

Платформы для выбора — C# или Delphi (т.к. есть прототип, реализующий 90% потребного
функционала, но в режиме «спагетти»).

Если с Delphi всё понятно — средство загрузки/выгрузки плагинов делать руками, то
вот с C# не всё так прозрачно.

Пока что быстрый поиск показал следующие варианты:


MEF: довольно просто в реализации, но нельзя изолировать плагины в отдельных AppDomain
(что довольно-таки важно, это существенное архитектурное преимущество .NET вообще и
шарпа в частности);
MAF: переусложнено, замудрённо, и, с виду, уже не поддерживается. С другой стороны —
мощная изоляция;
самостоятельная реализация на reflection (например, так), с возможностью загрузки
в AppDomain и прочим. Но придётся много велосипедить, чего в общем-то не хочется.


Есть ещё варианты? Может, что-то не было учтено?

UPD: добавил ссылки.
    


Ответы

Ответ 1



Для C# есть встроенное готовое решение от Microsoft: System.Addin. Позволяет конфигурировать плагины так, чтобы использовать их в том же AppDomain'е, или в выделенном другом, или каждый в своём, или вообще в отдельном процессе. Не понимаю, почему везде советуют MEF. Это решение именно для расширяемости: код, который вы загружаете, считается доверенным и очень мало отличается от основного кода приложения, поэтому возможности выгрузки и управление правами напрочь отсутствуют. Самостоятельный велосипед на reflection написать можно, но придётся довольно серьёзно разбираться в тонкостях наподобие Fusion и Load context, так что я бы не советовал тратить время. Обновление: Ага, System.Addin — это то, что вы называете MAF.

Ответ 2



Зависит от специфических требований к вашей системе. Так ли нужна изоляция в отдельный домен? Если код ваших плагинов не предполагает взаимодействия с Native библиотеками, то и исключения, выбрасываемые из плагина, не поломают вашу программу целиком. Вы вполне можете их перехватить, по стеку установить плагин-виновник и предложить пользователю в следующий раз не загружать его. Ну да, не так круто, как в динамике выгрузить домен, но зато насколько меньше возни. Вот Студия же работает именно так. Ну а если ваши плагины способны нарушить работу процесса на уровне ОС, то ляжет весь процесс приложения вместе с CLR и доменами. Поэтому с MAF я бы не связывался. Что до остального, могу посоветовать Mono.Addins. По возможностям превосходит MEF. Документация есть, но не исчерпывающая. Примеров маловато. Также есть система ICSharpCode.Core, используемая в SharpDevelop, она тоже мощная, есть исходники (сам шарпдевелоп). Но документации почти нет, и она сильно устарела.

среда, 26 февраля 2020 г.

Возможно ли создать аналог “Open With” на Java?

#java #desktop


В Windows есть функциональность "Open With"



Мне интересно, можно ли создать аналог "Open With" на Java? Допустим, у нам задан файл. 


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

    


Ответы

Ответ 1



Кроссплатформенно есть/был проект JDIC - но последний коммит у них там был год назад и страница с загрузками пуста. Для вызова непосредственно под Windows можно воспользоваться функцией из shell32.dll, вызвав ее из командной строки или через JNI. В простейшем случае: Runtime.getRuntime().exec("rundll32.exe shell32.dll,OpenAs_RunDLL c:\\file.txt");

Ответ 2



Для Windows вы можете создать из Java-программы exe-шник. Так пользователям будет намного проще. В интернете полно программ, делающих из Java-программы exe-шники. Тут уже было обсуждение.

Ответ 3



сам только недавно начал изучать это, приведенный ниже код работает: JFileChooser chooser = new JFileChooser(); // Note: source for ExampleFileFilter can be found in FileChooserDemo, // under the demo/jfc directory in the Java 2 SDK, Standard Edition. ExampleFileFilter filter = new ExampleFileFilter(); filter.addExtension("jpg"); filter.addExtension("gif"); filter.setDescription("JPG & GIF Images"); chooser.setFileFilter(filter); int returnVal = chooser.showOpenDialog(parent); if(returnVal == JFileChooser.APPROVE_OPTION) { System.out.println("You chose to open this file: " + chooser.getSelectedFile().getName()); } извини, не могу разжевать что за что отвечает, так как еще не знаю, но думаю тут найдутся ребята которые смогут.

Ответ 4



это не совсем то что вам нужно, но возможно будет полезным: Создаем файлик Test.java с вот таким содержимым: public class Test { public static void main (String []args) { System.out.println("hello world"); } } 1) создаем привязку .java файла к какой-то программе напр. eclipse 1.1) для этого в проводнике выбираем открыть с помощью... 1.2) указываем полный путь до программы и ставим галочку запомнить (чтоб он всегда открывался этой программой) 2.1) запускаем regedit 2.2) открываем ветку HKEY_CLASSES_ROOT\.java 2.3) смотрим значение ключа (Default), у меня это 'java_auto_file' 2.4) ищем в этой же ветке 'java_auto_file', у меня этот ключ имеет примерно следующее содержимое java_auto_file-->shell-->open-->command: (Default)="D:\programming\java\eclipse12\eclipse.exe" "%1" 3) Создаем свою строчку в контекстном меню 3.1) Создаем еще одну ветку с тем же содержимым: java_auto_file-->shell-->Compile-->command (Default)="D:\programming\java\eclipse12\eclipse.exe" "%1" 3.2) Заменяем путь к программе на путь к java компилятору: (Default)="C:\Program Files\Java\jdk1.7.0\bin\javac.exe" "%1" 4) компилируем класс 4.1) в проводнике из контекстного меню нашего файла выбираем пункт "compile" (тот который мы добавили) 4.2) существующий пункт open можно удалить при желании аналогично для .class файла действием по умолчанию можно сделать его запуск

вторник, 25 февраля 2020 г.

Java, Scala, Groovy и проч. что выбрать для разработки десктопа? [закрыт]

#groovy #desktop #java #scala


        
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он соответствовал тематике «Stack Overflow на русском».
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
Есть запрос на написание десктопного мультиплатформенного приложения. Условие язык
должен быть на платформе Java. 
Писать надо быстро, так что надо чтобы язык имел приличную гуйную библиотеку.
Что посоветуете?
По C/C++/Perl и проч. просьба не умничать. У приложения есть довольно большой набор
Java библиотек с бизнес-логикой, так что платформа строго Java - вариантов нет.    


Ответы

Ответ 1



Сами по себе Scala и Groovy пока не содержат в себе отдельных production-ready решений для GUI, кроме оберток над swing. При этом вариантов, вообще говоря, два - SwingBuilder для Groovy и scala.swing для Scala. Если вы хорошо знакомы с Groovy / Scala (и если вы не единственный из команды, кто может этим похвастаться), то можете воспользоваться одним из этих подходов. При этом, очевидно, стоит учитывать время на необходимость разобраться в деталях соответствующих оберток и возможные риски из-за их непродуманности или недоделанности. Очень обидно будет нарваться на UnsupportedOperationException("Not yet implemented") в конце спринта. Если нет, а выбор между vanilla Java / Scala / Groovy появился только благодаря "модности" последних, то работайте через них с java.swing, либо вообще остановитесь на просто Java. Лично я, не будь у меня как минимум года работы со Scala / Groovy / Clojure / Kotlin, не стал браться за разработку бизнес-решения на их базе.

Ответ 2



Наткнулся сегодня на dzone на номер журнала Java Tech Journal, посвященный Groovy, а уже в нем на статью о Grails-подобной платформе для десктопных приложений Griffon. Что дают (если верить статье): Набор утилит командной строки для создания проекта, сборки, упаковки и деплоя MVC Bindings для свойств бинов различные реализации непосредственно GUI (Swing, SWT, JavaFX) расширяемость плагинами Convention over Configuration, управление структурой и жизненным циклом приложения В общем, если есть тяга к экспериментам, я бы взглянул.

Ответ 3



Если надо быстро написать десктопное мультиплатформенное приложение, сегодня это возможно только на Lazarus+freepascal, который к тому же очищает совесть своим openSource. Слова "быстро написать", "java" и "мультиплатформенное приложение" не вяжутся. Сказки про java просто хорошо раскручены.

четверг, 13 февраля 2020 г.

Desktop API не поддерживается в Linux

#linux #java #desktop


Задача в том, чтобы открывать файл или папку из java-приложения. Воспользовался советом
на форуме. Но оказывается это не работает в Linux, т.к. Desktop.isDesktopSupported()
== false. Как сделать, чтобы программа заработала и на Linux тоже?
    


Ответы

Ответ 1



Как-нибудь так: try { if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { Desktop.getDesktop().browse(new URI(url)); } else { string os = System.getProperty("os.name"); if (!(os.startsWith("Mac OS")) && !(os.startsWith("Windows"))) { Runtime r = Runtime.getRuntime(); for (String b : list /* google-chrome, firefox, opera */) { Process p = r.exec(new String[]{"which", b}); InputStream is = p.getInputStream(); if (is.read() != -1) { r.exec(new String[]{b, url}); return; } } } } } catch (Throwable t) { }

Ответ 2



Согласно информации с сайта oracle, в системе Linux данный функционал доступен только при наличии библиотек gnome. Если эти библиотеки не установлены, метод возвращает false. Mожно попробовать просто запускать необходимую программу. Что то вроде: Runtime.getRuntime().exec("/usr/bin/firefox www.hashcode.ru");

воскресенье, 26 января 2020 г.

Какие языки программирования хороши для создания кросс-платформенных desktop приложений? [закрыт]

#кроссплатформенность #desktop


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 3 года назад.
                                                                                
           
                
        
Смотрел Java. В x64 системе окошко с хелловорлдом отъедает около 20 МБ памяти, и
это число быстро растёт при создании новых объектов. Mono - достаточно посмотреть на
плеер Banshee чтобы понять что там ситуация не лучше (Музыкальный плеер отъедает ~
100 МБ памяти, при том что я всегда держу загруженный плеер мне это не нравится).
С++/Qt кажется неплохо, но на изучение придётся потратить большое кол-во усилий,
при том что не известно оправдаются ли(Вообще у Qt сейчас как я вижу не самая стабильная
ситуация, да и с плюсами в новых проектах люди стали поосторожнее)
Python/PyGTK оказался на удивление более скромным к памяти чем java, но сам язык
кажется мне каким-то невменяемым, возможно я просто не понимаю динамичскую типизацию.
Отсутствие инкапсуляции и перегрузки функций удивляет. Непонятно какое значение возвращает
какая-либо функция. Код показался не читаемым как раз из-за отсутствия скобок. Где-то
наткнулся на совет реже использовать вызовы функций, особенно в циклах т.к. это дорогая
операция в питоне. Перед глазами сразу представились огромные методы в 200+ строк.
PS Возможно я в чём-то не прав т.к. посмотрел на эти языки весьма поверхностно. Кросс-платформенность
на уровне компиляции вполне устроила бы(с неизменным кодом).    


Ответы

Ответ 1



Что лучше зависит от критериев выбора. В данном случае они такие: Время разработки и время внесения изменений (причем второе может быть важней первого для заказных приложений). Сложность приложения. Тип приложения("фотошоп", "WoW" или "клиент к твиттеру"), тиражное или заказное. С++ скорость разработки самая низкая для сложных приложений для тиражных решений требующих высокой оптимизации выполнения. Для слабого железа - GPS навигаторы, банкоматы, станки с ЧПУ, ... Java время разработки меньше чем у C++. Особенно время внесения изменений, доработок для сложных приложений когда требуется эффективность выполнения программы (перерасход памяти беспокоит только молодых программистов. Ни производителей смартфонов на Android, ни заказчиков бизнес софта это свойство JVM не волнуют. И мою жену тоже не волнует сколько отъедает музыкальный плеер), и при этом не требуется оптимизация выполнения с помощью низкоуровневых трюков или специфики ОС. Python время разработки самое низкое для простой и средней сложности приложений (для сложных - только при высокой квалификации программистов или формализованного процесса разработки) для некритичных к времени выполнения(примеры - клиент Dropbox'а, wikidpad). Для заказных, малотиражных приложений. (программы на серверах гугла и яндекса - "заказные", потому что уникальны) О времени разработки молодые программисты обычно не задумываются. А частенько в жизни - побеждает не лучшее, а - первое. Особенно при работе под заказ. О динамической типизации. Она требует самодисциплины разработки, чего обычно нет у молодых программистов, и они сами себе "злые буратины" быстро превращающие код в полный хаос. Она требует обязательного и сурового применения TDD или подобных методик. Интерпретатор/компилятор ЯП с динамической типизацией никогда не сможет выполнять/генерировать код той же эффективности выполнения что с статической типизацией. К вопросу - нравится/не нравится какой-то язык программирования. Во-первых, часто "не нравится" это - "не знал, не знаю, и знать не хочу" Во-вторых, когда после нескольких законченных и работающих программ остается "не нравится" то действительно стоит подумать о смене языка программирования. Разработка на любимом языке программирования - очень важный мотиватор для уменьшения времени разработки и улучшении ее качества. (О себе - профессионально(то есть за деньги) писал на plain С, Java, Python)

Ответ 2



Отвечу про Python. Отсутствие инкапсуляции и перегрузки функций удивляет. Инкапсуляция в питоне, хоть и весьма условная, но есть. Если метод начинается с __, то интерпретатор автоматически добавляет к имени префикс _%current_class% и, соответственно, в другом классе такой метод уже не будет виден. Private Methods: """ >>> obj = bar() >>> obj.__test() Traceback (most recent call last): ... AttributeError: 'bar' object has no attribute '__test' >>> obj.test2() Traceback (most recent call last): ... AttributeError: 'super' object has no attribute '_bar__test' >>> obj._foo__test() Hello >>> obj._foo__test > """ import doctest class foo(): def __test(self): print('Hello') class bar(foo): def test2(self): super().__test() doctest.testmod(optionflags=doctest.ELLIPSIS) Таким образом достигается инкапсуляция атрибутов класса. Конечно-же, все еще можно обратится к такому методу по полному имени. Однако, для примера, и в C# можно обратиться к приватным методам через рефлексию, но это вовсе не значит, что эти методы публичные. Для создания абстрактных классов используется специальный метакласс c декораторами. """ >>> obj = bar() >>> obj.test() Hello >>> obj = baz() Traceback (most recent call last): ... TypeError: Can't instantiate abstract class baz with abstract methods test """ import abc import doctest class foo(metaclass=abc.ABCMeta): @abc.abstractmethod def test(self): print('Hello') class bar(foo): def test(self): super().test() class baz(foo): pass doctest.testmod() Аналогичным образом реализуются и интерфейсы. Что-же касается перегрузок функций, то в динамическом языке, где методы могут принимать переменное число параметров и значения по умолчанию - это не возможно, да и совершенно не нужно. Пример: """ >>> foo(1, 2, 3, 4, 5, arg1=1, arg2=2, bar=False) (1, 2, (3, 4, 5), False, {'arg1': 1, 'arg2': 2}) """ import doctest def foo(first, second, *args, bar=True, **kwargs): print( (first, second, args, bar, kwargs) ) doctest.testmod() Непонятно какое значение возвращает какая-либо функция Да, этот недостаток присущ, наверное, всем динамическим языкам. Однако, в оправдание питона могу сказать, что эта проблема частично решается при помощи аннотаций, полноценного IDE и хорошего документирования функций. Более того, при помощи аннотаций, метаклассов и декораторов не сложно реализовать и статическую типизацию :) Т.е. примерно следующее: """ >>> foo().bar([1, 2, 3]) Traceback (most recent call last): ... TypeError: 'param' is 'list', but the expected type is 'int' >>> foo().bar(1) Traceback (most recent call last): ... TypeError: function return is 'str', but the expected type is 'bool' """ class foo(metaclass=StrongTyping): def bar(param: int) -> bool: return 'some text' Разумеется, что бы пример работал, нужно еще реализовать метакласс StrongTyping. Код показался не читаемым как раз из-за отсутствия скобок. Вероятно, это дело привычки. Отсутствие скобок обязывает программиста следить за отступами и использовать единый символ табуляции, что уже, на мой взгляд, не мало. Что же касается визуального разделения методов, то тут помогаю IDE, которые отделяют их горизонтальными линиями. Где-то наткнулся на совет реже использовать вызовы функций, особенно в циклах т.к. это дорогая операция в питоне. Перед глазами сразу представились огромные методы в 200+ строк. На мой взгляд, гибкость питона и такие возможности, как генераторы выражений наоборот позволяют писать очень компактные и лаконичные конструкции. Там где для строго-типизированных языков требуются куча циклов и условных конструкций, в питоне решается одной строчкой. UPD. Какие преимущества у динамической типизации? Имхо, именно в динамичности. Где еще можно написать нечто подобное: """ >>> op = ImportantClass() >>> op.foo(1) >>> ImportantClass.debug(True) >>> op.foo(2) Called (<__main__.ImportantClass object at 0x...>, 2) {} None """ import doctest import functools def logging(func): """ Декоратор функции, который логирует все вызовы """ @functools.wraps(func) def wrapper(*args, **kwargs): ret = func(*args, **kwargs) print('Called ', func, args, kwargs, ret) return ret # одна из особенностей питона - это то, что все является объектами # добавляем атрибут к функции wrapper.original = func return wrapper def debugging(cls): """ Декоратор класса, добавляет метод debug(enable) """ @classmethod def debug(cls, enable=True): attr = '__call__' if enable else 'original' call = lambda f: logging(f) if enable else f.original # в цикле подменяются все функции на logging(f), либо на оригинальные, # которые хранятся в декораторе for func in [call(f) for f in cls.__dict__.values() if hasattr(f, attr)]: setattr(cls, func.__name__, func) # добавляем новый метод для декорируемого класс # именно для класса, декоратор вызывается только один раз cls.debug = debug return cls @debugging class ImportantClass(): def foo(self, *args, **kwargs): pass # do something important def bar(self, *args, **kwargs): pass # also do something doctest.testmod(optionflags=doctest.ELLIPSIS) Декораторы logging и debugging можно вынести в отдельный модуль и применять для любых классов. При этом при выключенной отладке накладных расходов практически не будет, т.к. методы именно заменяеются другими методоми, а не оборачиваются. С другой стороны именно эта динамичность может служить источником ошибок, по этому приходится использовать ее осторожно и не забывать про тестирование. Кстати,на самом деле считается, что питон имеет одновременно и строгую, и динамическую типизацию. Т.н. код "1" + 1 выбросит исключение TypeError.

Ответ 3



Qt сама по себе не так сложна, если знакомы с С++. Зато возможности мощнейшие. Кросплатформенность тотальная! Ценю Qt еще и за то, что компоновка внешнего вида объектов происходит почти автоматом - не получается аляпистых кривых окон (как это часто любят делать программеры делфи). Для любой ОС внешний вид приложения будет идентичен - можно не опасаясь писать под виндой приложение которое будет потом скомпилировано и под Linux, и оно будет выглядеть одинаково и там и там. Мощнейший набор классов на все случаи жизни - мультимедиа, графика, текстовые редакторы, базы данных и т.д. Стабильность и поддержка очень крупной компанией. Регулярные релизы. Наличие свободной версии и версии для мобильных устройств. Сложность изучения языка и самого набора библиотек окупается многократно.

Ответ 4



Свой сверхвысокоуровневый DSL (Domain Specific Language, заточенный под ваши задачи) + автогенерация кода для (полусотни) целевых платформ х (PC *2 /win,lin/ *2 (x32,x64), Mac, Android *2 /4.x, backport-затычки 2.x/, iPhone, смарт-часы, облака х количество СУБД/хранилищ данных каждая со своими заморочками х Java, С#, JavaScript и еще десяток ходовых ЯП потому что понадобилась какая-то уникальная библиотека или работа приложения в специфическом окружении, или заказчик ограничил т.к. у него есть спецы для поддержки знающие что-то одно. По универсальности/эффективности и доступности вотпрямщщас: связка Python и С++, и нативные библиотеки GUI, интерфейса к БД итд, завернутые в классы с одним интерфейсом (для каждого вида -- GUI, БД,..).

среда, 25 декабря 2019 г.

Как сделать ключевые слова на клавишах как в Spectrum?

#linux #gui #desktop #клавиатура


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

Например, нажал клавишу P, а напечаталось слово PRINT

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

Видел, что в xkb есть некие actions (в compatibility map), но в списке доступных
я не нашел замены одной клавиши на последовательность нажатий.

Будет ли хорошим решением добавить новую разновидность action, или есть другие варианты?
    


Ответы

Ответ 1



можно воспользоваться механизмом xcompose, предварительно определив в настройках клавиатуры клавишу, используемую в качестве compose key. пример файла ~/.XCompose:

: "PRINT" нажимаете и отпускаете compose key, затем shift+p, и получаете слово PRINT. примеры правил можно посмотреть в /usr/share/X11/locale/en_US.UTF-8/Compose

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

Как правильно организовать работу с данными (C# Desktop App, Database)?

#c_sharp #sqlite #desktop


Упрощенная схема БД


Нужна помощь в организации работы с данными.
Сабжект: десктопное приложение для гостиницы. 
В таблице Reservations данные по каждому размещению. Каждое размещение может состоять
из одного или нескольких дней (ResDays). На каждый день размещения может быть добавлена
доп. услуга (AddServices).

В программе для этого я использую коллекции. Очень упрощенно:

class AddServiceItem
{
   string Name;
   Decimal Cost;
}
class ResDayItem
{
   DateTime Date;
   Decimal Cost;
   Collection AddServices;
}
class Reservation
{
   DateTime StartDate;
   DateTime StartDate;
   int Status;
   Collection ResDays;
}


С созданием размещения проблем нет - пользователь выбирает количество дней, создается
экземпляр Reservation, а коллекции из дней и доп. услуг заполняются юзером в интерфейсе
программы. После нажатия кнопки "Добавить размещение" все записывается в БД.

Конечно, нужно иметь возможность изменять все данные. При открытии размещения для
редактирования, я опять создаю экземпляр Reservation, а коллекции заполняю значениями
из БД примерно так

MyReservation = new Reservation();
//создаю коллекцию дней
MyReservation.ResDays = new Collection();
//и для каждого дня размещения с Id == resId
foreach (var Day in db.Table().Where(x => x.ResId == resId))
{   //создаю экземпляр ResDayItem
    ResDayItem DayItem = new ReservationDayItem();
  //и в нем коллекцию доп. услуг
  DayItem.AddServices = new Collection();
    foreach (var AddService in db.Table().Where(x => x.ResDayId == Day.Id))
        DayItem.AddServices.Add(AddService);
    MyReservation.ResDays.Add(DayItem);
}


Вопрос в следующем: как (в каком порядке) делать апдейт БД при изменении коллекций?
Хотелось бы, чтобы в БД все писалось при нажатии пользователем кнопки "Сохранить".
Если количество дней/доп.услуг осталось прежним, то это просто update по всем записям,
а если юзер удалил что-то? Как узнать, какие именно элементы коллекций были удалены,
чтобы удалить соответствующие записи из БД?
Может быть я вообще применил неверный подход с такой вложенностью коллекций?
Как правильно организовать работу с данными в такой задаче?
    


Ответы

Ответ 1



В более-менее сложных программах реализуют примерно такую структуру Как пример реализации паттернов Repository и Unit of Work могу предложить вам следующий вариант. Для Repository мы будем опираться на следующую иллюстрацию Для Unit of Work мы будем опираться на следующую иллюстрацию 1) Создайте проект Class Library LibDomain, создайте в нем две папки: Repositories и Models. 2) Cоздайте след. классы в папке Models public class Reservation { //ctor public Reservation() => Days = new HashSet(); public long Id { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public int Status { get; set; } public virtual ICollection Days { get; set; } } `` public class ReservedDay { //ctor public ReservedDay() => Services = new HashSet(); public long Id { get; set; } public DateTime Date { get; set; } public decimal Cost { get; set; } //внешний ключ public long ReservationId { get; set; } //навигационное св-во public virtual Reservation Reservation { get; set; } public ICollection Services { get; set; } } `` public class AdditionService { public int Id { get; set; } public string Name { get; set; } public decimal Cost { get; set; } //внешний ключ public long ReservedDayId { get; set; } //навигационное св-во public virtual ReservedDay Day { get; set; } } `` 3) в папке репозиториев вам нужно будет создать главный интерфейс репозитория public interface IRepository where TEntity : class { //Queries Task> Get(); Task Get(long id); Task> Find(Expression> predicate); //Commands Task Add(TEntity entity); Task AddRange(IEnumerable entities); void Remove(TEntity entity); void RemoveRange(IEnumerable entities); } и на каждую сущность свой интерфейса репозитория (даю пример только двух) public interface IReservationRepository : IRepository { //здесь можно добавить объявление необходимых методов //актуальных для данной сущности Task GetByIdWithDays(long id); Task GetByIdWithDaysAndServices(long id); } `` public interface IReservedDayRepository : IRepository { //здесь можно добавить объявление необходимых методов //актуальных только для данной сущности } создайте в корне проекта интерфейс для UnitOfWork public interface IUnitOfWork : IDisposable { IReservationRepository Reservations { get; } IReservedDayRepository ReservedDays { get; } IAdditionServiceRepository AdditionServices { get; } Task Commit(); } 4) Создайте в решении еще один проект библиотеки LibData, в котором мы непосредственно реализуем работу с SQLite через Entity Framework Core. Добавьте ссылку на LibDomain и установите через NuGet Package Manager пакет Microsoft.EntityFrameworkCore.Sqlite (важно! устанавливайте версию 1.1.2, а не самую последнюю 2.0, которая вышла несколько дней назад, иначе будут проблемы). 5) в проекте создайте класс SqliteDbContext public class SqliteDbContext : DbContext { //ctor public SqliteDbContext(DbContextOptions options) : base(options) { //создание БД, если она не была ранее создана Database.EnsureCreated(); } public virtual DbSet Reservations { get; set; } public virtual DbSet ReservedDays { get; set; } public virtual DbSet AdditionServices { get; set; } } и создайте папку Repositories и в ней класс репозитория public abstract class Repository : IRepository where TEntity : class { protected readonly DbContext _context; //ctor public Repository(DbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); } //Queries public virtual async Task> Get() { return await _context.Set().ToArrayAsync(); } public virtual async Task Get(long id) { return await _context.Set().FindAsync(id); } public virtual async Task> Find(Expression> predicate) { return await _context.Set().Where(predicate).ToArrayAsync(); } //Commands public virtual Task Add(TEntity entity) { return _context.AddAsync(entity); } public virtual Task AddRange(IEnumerable entities) { return _context.AddRangeAsync(entities); } public virtual void Remove(TEntity entity) { _context.Remove(entity); } public virtual void RemoveRange(IEnumerable entities) { _context.RemoveRange(entities); } } и классы для каждой сущности: ReservationRepository, ReservedDayRepository, AdditionServiceRepository (пример только для первого класса) public class ReservationRepository : Repository, IReservationRepository { private readonly SqliteDbContext _context; //ctor public ReservationRepository(SqliteDbContext context) : base(context) { _context = context; } //далее реализация методов из IReservationRepository //или переопределение методов из Repository под Reservation public Task GetByIdWithDays(long id) { return _context.Reservations .Include(r => r.Days) .FirstAsync(r => r.Id == id); } public Task GetByIdWithDaysAndServices(long id) { return _context.Reservations .Include(r => r.Days) .ThenInclude(d => d.Services) .FirstAsync(r => r.Id == id); } } 6) создайте класс, кот. реализутет интерфейс IUnitOfWork public class SqliteDataService : IUnitOfWork { private readonly SqliteDbContext _context; //ctor public SqliteDataService(SqliteDbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); Reservations = new ReservationRepository(_context); ReservedDays = new ReservedDayRepository(_context); AdditionServices = new AdditionServiceRepository(_context); } public IReservationRepository Reservations { get; private set; } public IReservedDayRepository ReservedDays { get; private set; } public IAdditionServiceRepository AdditionServices { get; private set; } public Task Commit() => _context.SaveChangesAsync(); public void Dispose() => _context.Dispose(); /// /// Соединение с БД и передача экземпляра этого сервиса /// /// строка соединения с БД /// экземпляр этого сервиса public static SqliteDataService GetService(string connectionString) { if (String.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString)); var dbBuilder = new DbContextOptionsBuilder(); dbBuilder.UseSqlite(connectionString); return new SqliteDataService(new SqliteDbContext(dbBuilder.Options)); } } 7) Пересмотрите еще раз иллюстрации, перечитайте код примеров. Надеюсь вам станет яснее схема взаимодействия с БД. В качестве примера использования привожу пример простого консольного приложения class Program { static void Main(string[] args) { string dbPath = @"Filename=D:\repExample.sqlite"; var dbService = SqliteDataService.GetService(dbPath); //AddReservation(dbService); //AddServicesToReservation(dbService); ShowReservation(dbService); Console.ReadKey(); } private static async void ShowReservation(SqliteDataService dbService) { //находим размещение c его днями и услугами по его id var res = await dbService.Reservations.GetByIdWithDaysAndServices(1); Console.WriteLine(); Console.WriteLine($"Размещение с {res.StartDate.ToShortDateString()} по {res.EndDate.ToShortDateString()}"); Console.WriteLine(new string('=', 70)); int dayNumber = 1; int servNumber = 1; foreach (var day in res.Days) { Console.WriteLine(); Console.WriteLine($"День {dayNumber} - {day.Date.ToShortDateString()}"); foreach (var service in day.Services) { Console.WriteLine(); Console.WriteLine($"Услуга {servNumber}:{service.Name} за {service.Cost}руб."); servNumber++; } Console.WriteLine(new string('-', 30)); dayNumber++; servNumber = 1; } } private static async void AddReservation(SqliteDataService dbService) { //создаем дни размещения ReservedDay day1 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 20), Cost = 200M }; ReservedDay day2 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 21), Cost = 200M }; ReservedDay day3 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 22), Cost = 200M }; //размещение и его дни Reservation res = new Reservation() { StartDate = day1.Date, EndDate = day3.Date, Status = 1 }; res.Days.Add(day1); res.Days.Add(day2); res.Days.Add(day3); //добавляем и сохраняем в БД await dbService.Reservations.Add(res); await dbService.Commit(); } private static async void AddServicesToReservation(SqliteDataService dbService) { var serv1 = new AdditionService { Name = "Бассейн", Cost = 2M }; var serv2 = new AdditionService { Name = "Парилка", Cost = 1M }; var serv3 = new AdditionService { Name = "Завтрак в номер", Cost = 100M }; var serv4 = new AdditionService { Name = "Чай в номер", Cost = 10M }; //находим размещение по его id var res = await dbService.Reservations.GetByIdWithDays(1); //выбираем дни по дате var dateDay1 = new DateTime(year: 2017, month: 8, day: 20); var dateDay2 = new DateTime(year: 2017, month: 8, day: 21); var dateDay3 = new DateTime(year: 2017, month: 8, day: 22); var day1 = res.Days.Single(d => d.Date == dateDay1); var day2 = res.Days.Single(d => d.Date == dateDay2); var day3 = res.Days.Single(d => d.Date == dateDay3); //добавляем услуги в эти дни day1.Services.Add(serv1); day1.Services.Add(serv4); day2.Services.Add(serv3); day2.Services.Add(serv1); day2.Services.Add(serv2); day3.Services.Add(serv4); //запоминаем await dbService.Commit(); } } Целиком пример можно скачать здесь.

суббота, 21 декабря 2019 г.

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

#java #desktop


У меня есть расширение файла. Надо получить изображение, которое является иконкой
для данного расширения в операционной системе пользователя. Как это можно сделать?
Файла как такового у нас нет, известно только его расширение.    


Ответы

Ответ 1



Это невероятно, но я нашёл нужную Вам фичу в Джаве. Под Виндоус работает. Под Макосью сейчас нет возможности проверить, но догадываюсь, что тоже работать будет. Под Линуксом всё плохо :(.

Ответ 2



Ок, я нашёл почти универсальный способ. В принципе, я мог бы сразу догадаться, что нужная фича есть в Qt ;). Вам сюда и далее по тексту. Привязка Qt к Джаве тут. Под Windows и MacOS это будет работать 100%. Под Линуксом, как обычно, свои заморочки, но можно надеяться, что как-нибудь сработает :). Естественно, серьёзный недостаток — придётся таскать за собой Qt. Но тут уж либо так, либо никак ;).

Ответ 3



В Windows всё это можно достать из реестра. Простого пути нет, так что придётся немного напрячься. Вот здесь описано, что и как делать и есть пример кода на C#. Думаю, перевести его на Java будет не сильно сложно. Готовое решение на Java найдётся едва ли.

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

Как открыть файл или папку из java-приложения?

#java #desktop


Каким образом можно открыть файл или папку, как будто мы открываем его через стандартный
файловый браузер / Finder / и т.д.    


Ответы

Ответ 1



Стоит обратить внимание на Desktop API. Вот статья, подробно описывающая его. Инициализация: Desktop desktop = null; if (Desktop.isDesktopSupported()) { desktop = Desktop.getDesktop(); } Открытие файла: try { desktop.open(new File(fileName)); } catch (IOException ioe) { ioe.printStackTrace(); }

среда, 5 июня 2019 г.

Создание ПО с плагинами

По долгу службы требуется реализация довольно сложного десктопного ПО. Как одно из требований — плагины (подключаемые модули), которые могут быть очень разнообразными: от определения новых типов данных до работы с интерфейсом (добавление форм, кнопок и т.п.). Разумеется, также требуются аналитика, обработка данных, сохранение в разные БД и т.д.
Платформы для выбора — C# или Delphi (т.к. есть прототип, реализующий 90% потребного функционала, но в режиме «спагетти»).
Если с Delphi всё понятно — средство загрузки/выгрузки плагинов делать руками, то вот с C# не всё так прозрачно.
Пока что быстрый поиск показал следующие варианты:
MEF: довольно просто в реализации, но нельзя изолировать плагины в отдельных AppDomain (что довольно-таки важно, это существенное архитектурное преимущество .NET вообще и шарпа в частности); MAF: переусложнено, замудрённо, и, с виду, уже не поддерживается. С другой стороны — мощная изоляция; самостоятельная реализация на reflection (например, так), с возможностью загрузки в AppDomain и прочим. Но придётся много велосипедить, чего в общем-то не хочется.
Есть ещё варианты? Может, что-то не было учтено?
UPD: добавил ссылки.


Ответ

Для C# есть встроенное готовое решение от Microsoft: System.Addin. Позволяет конфигурировать плагины так, чтобы использовать их в том же AppDomain'е, или в выделенном другом, или каждый в своём, или вообще в отдельном процессе.
Не понимаю, почему везде советуют MEF. Это решение именно для расширяемости: код, который вы загружаете, считается доверенным и очень мало отличается от основного кода приложения, поэтому возможности выгрузки и управление правами напрочь отсутствуют.
Самостоятельный велосипед на reflection написать можно, но придётся довольно серьёзно разбираться в тонкостях наподобие Fusion и Load context, так что я бы не советовал тратить время.

Обновление: Ага, System.Addin — это то, что вы называете MAF.

среда, 15 мая 2019 г.

Возможно ли создать аналог “Open With” на Java?

В Windows есть функциональность "Open With"

Мне интересно, можно ли создать аналог "Open With" на Java? Допустим, у нам задан файл.
Нужно получить список "рекомендованных" программ для открытия файла. Нужно оставить возможность пользователю выбрать произвольную программу. Иначе предложить пользователю скачать программу через интернет. Попытаться открыть файл (так, как указал пользователь).


Ответ

Кроссплатформенно есть/был проект JDIC - но последний коммит у них там был год назад и страница с загрузками пуста. Для вызова непосредственно под Windows можно воспользоваться функцией из shell32.dll, вызвав ее из командной строки или через JNI. В простейшем случае: Runtime.getRuntime().exec("rundll32.exe shell32.dll,OpenAs_RunDLL c:\\file.txt");

вторник, 14 мая 2019 г.

Java, Scala, Groovy и проч. что выбрать для разработки десктопа? [закрыт]

Есть запрос на написание десктопного мультиплатформенного приложения. Условие язык должен быть на платформе Java. Писать надо быстро, так что надо чтобы язык имел приличную гуйную библиотеку. Что посоветуете? По C/C++/Perl и проч. просьба не умничать. У приложения есть довольно большой набор Java библиотек с бизнес-логикой, так что платформа строго Java - вариантов нет.


Ответ

Сами по себе Scala и Groovy пока не содержат в себе отдельных production-ready решений для GUI, кроме оберток над swing. При этом вариантов, вообще говоря, два - SwingBuilder для Groovy и scala.swing для Scala Если вы хорошо знакомы с Groovy / Scala (и если вы не единственный из команды, кто может этим похвастаться), то можете воспользоваться одним из этих подходов. При этом, очевидно, стоит учитывать время на необходимость разобраться в деталях соответствующих оберток и возможные риски из-за их непродуманности или недоделанности. Очень обидно будет нарваться на UnsupportedOperationException("Not yet implemented") в конце спринта. Если нет, а выбор между vanilla Java / Scala / Groovy появился только благодаря "модности" последних, то работайте через них с java.swing, либо вообще остановитесь на просто Java Лично я, не будь у меня как минимум года работы со Scala / Groovy / Clojure / Kotlin, не стал браться за разработку бизнес-решения на их базе.

понедельник, 18 февраля 2019 г.

Как правильно организовать работу с данными (C# Desktop App, Database)?

Упрощенная схема БД
Нужна помощь в организации работы с данными. Сабжект: десктопное приложение для гостиницы. В таблице Reservations данные по каждому размещению. Каждое размещение может состоять из одного или нескольких дней (ResDays). На каждый день размещения может быть добавлена доп. услуга (AddServices).
В программе для этого я использую коллекции. Очень упрощенно:
class AddServiceItem { string Name; Decimal Cost; } class ResDayItem { DateTime Date; Decimal Cost; Collection AddServices; } class Reservation { DateTime StartDate; DateTime StartDate; int Status; Collection ResDays; }
С созданием размещения проблем нет - пользователь выбирает количество дней, создается экземпляр Reservation, а коллекции из дней и доп. услуг заполняются юзером в интерфейсе программы. После нажатия кнопки "Добавить размещение" все записывается в БД.
Конечно, нужно иметь возможность изменять все данные. При открытии размещения для редактирования, я опять создаю экземпляр Reservation, а коллекции заполняю значениями из БД примерно так
MyReservation = new Reservation(); //создаю коллекцию дней MyReservation.ResDays = new Collection(); //и для каждого дня размещения с Id == resId foreach (var Day in db.Table().Where(x => x.ResId == resId)) { //создаю экземпляр ResDayItem ResDayItem DayItem = new ReservationDayItem(); //и в нем коллекцию доп. услуг DayItem.AddServices = new Collection(); foreach (var AddService in db.Table().Where(x => x.ResDayId == Day.Id)) DayItem.AddServices.Add(AddService); MyReservation.ResDays.Add(DayItem); }
Вопрос в следующем: как (в каком порядке) делать апдейт БД при изменении коллекций? Хотелось бы, чтобы в БД все писалось при нажатии пользователем кнопки "Сохранить". Если количество дней/доп.услуг осталось прежним, то это просто update по всем записям, а если юзер удалил что-то? Как узнать, какие именно элементы коллекций были удалены, чтобы удалить соответствующие записи из БД? Может быть я вообще применил неверный подход с такой вложенностью коллекций? Как правильно организовать работу с данными в такой задаче?


Ответ

В более-менее сложных программах реализуют примерно такую структуру

Как пример реализации паттернов Repository и Unit of Work могу предложить вам следующий вариант. Для Repository мы будем опираться на следующую иллюстрацию

Для Unit of Work мы будем опираться на следующую иллюстрацию

1) Создайте проект Class Library LibDomain, создайте в нем две папки: Repositories и Models. 2) Cоздайте след. классы в папке Models
public class Reservation { //ctor public Reservation() => Days = new HashSet();
public long Id { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public int Status { get; set; }
public virtual ICollection Days { get; set; } }
``
public class ReservedDay { //ctor public ReservedDay() => Services = new HashSet();
public long Id { get; set; } public DateTime Date { get; set; } public decimal Cost { get; set; }
//внешний ключ public long ReservationId { get; set; } //навигационное св-во public virtual Reservation Reservation { get; set; }
public ICollection Services { get; set; } }
``
public class AdditionService { public int Id { get; set; } public string Name { get; set; } public decimal Cost { get; set; }
//внешний ключ public long ReservedDayId { get; set; } //навигационное св-во public virtual ReservedDay Day { get; set; } }
`` 3) в папке репозиториев вам нужно будет создать главный интерфейс репозитория
public interface IRepository where TEntity : class { //Queries Task> Get(); Task Get(long id); Task> Find(Expression> predicate);
//Commands Task Add(TEntity entity); Task AddRange(IEnumerable entities); void Remove(TEntity entity); void RemoveRange(IEnumerable entities); }
и на каждую сущность свой интерфейса репозитория (даю пример только двух)
public interface IReservationRepository : IRepository { //здесь можно добавить объявление необходимых методов //актуальных для данной сущности
Task GetByIdWithDays(long id);
Task GetByIdWithDaysAndServices(long id); }
``
public interface IReservedDayRepository : IRepository { //здесь можно добавить объявление необходимых методов //актуальных только для данной сущности }
создайте в корне проекта интерфейс для UnitOfWork
public interface IUnitOfWork : IDisposable { IReservationRepository Reservations { get; } IReservedDayRepository ReservedDays { get; } IAdditionServiceRepository AdditionServices { get; }
Task Commit(); }
4) Создайте в решении еще один проект библиотеки LibData, в котором мы непосредственно реализуем работу с SQLite через Entity Framework Core. Добавьте ссылку на LibDomain и установите через NuGet Package Manager пакет Microsoft.EntityFrameworkCore.Sqlite (важно! устанавливайте версию 1.1.2, а не самую последнюю 2.0, которая вышла несколько дней назад, иначе будут проблемы).
5) в проекте создайте класс SqliteDbContext
public class SqliteDbContext : DbContext { //ctor public SqliteDbContext(DbContextOptions options) : base(options) { //создание БД, если она не была ранее создана Database.EnsureCreated(); }
public virtual DbSet Reservations { get; set; } public virtual DbSet ReservedDays { get; set; } public virtual DbSet AdditionServices { get; set; } }
и создайте папку Repositories и в ней класс репозитория
public abstract class Repository : IRepository where TEntity : class { protected readonly DbContext _context;
//ctor public Repository(DbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); }
//Queries public virtual async Task> Get() { return await _context.Set().ToArrayAsync(); }
public virtual async Task Get(long id) { return await _context.Set().FindAsync(id); }
public virtual async Task> Find(Expression> predicate) { return await _context.Set().Where(predicate).ToArrayAsync(); }
//Commands public virtual Task Add(TEntity entity) { return _context.AddAsync(entity); }
public virtual Task AddRange(IEnumerable entities) { return _context.AddRangeAsync(entities); }
public virtual void Remove(TEntity entity) { _context.Remove(entity); }
public virtual void RemoveRange(IEnumerable entities) { _context.RemoveRange(entities); } }
и классы для каждой сущности: ReservationRepository, ReservedDayRepository, AdditionServiceRepository (пример только для первого класса)
public class ReservationRepository : Repository, IReservationRepository { private readonly SqliteDbContext _context;
//ctor public ReservationRepository(SqliteDbContext context) : base(context) { _context = context; }
//далее реализация методов из IReservationRepository //или переопределение методов из Repository под Reservation
public Task GetByIdWithDays(long id) { return _context.Reservations .Include(r => r.Days) .FirstAsync(r => r.Id == id); }
public Task GetByIdWithDaysAndServices(long id) { return _context.Reservations .Include(r => r.Days) .ThenInclude(d => d.Services) .FirstAsync(r => r.Id == id); } }
6) создайте класс, кот. реализутет интерфейс IUnitOfWork
public class SqliteDataService : IUnitOfWork { private readonly SqliteDbContext _context;
//ctor public SqliteDataService(SqliteDbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context));
Reservations = new ReservationRepository(_context); ReservedDays = new ReservedDayRepository(_context); AdditionServices = new AdditionServiceRepository(_context); }
public IReservationRepository Reservations { get; private set; } public IReservedDayRepository ReservedDays { get; private set; } public IAdditionServiceRepository AdditionServices { get; private set; }
public Task Commit() => _context.SaveChangesAsync(); public void Dispose() => _context.Dispose();
///

/// Соединение с БД и передача экземпляра этого сервиса /// /// строка соединения с БД /// экземпляр этого сервиса public static SqliteDataService GetService(string connectionString) { if (String.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString));
var dbBuilder = new DbContextOptionsBuilder(); dbBuilder.UseSqlite(connectionString);
return new SqliteDataService(new SqliteDbContext(dbBuilder.Options)); } }
7) Пересмотрите еще раз иллюстрации, перечитайте код примеров. Надеюсь вам станет яснее схема взаимодействия с БД. В качестве примера использования привожу пример простого консольного приложения
class Program { static void Main(string[] args) {
string dbPath = @"Filename=D:
epExample.sqlite"; var dbService = SqliteDataService.GetService(dbPath);
//AddReservation(dbService); //AddServicesToReservation(dbService);
ShowReservation(dbService);
Console.ReadKey(); }
private static async void ShowReservation(SqliteDataService dbService) { //находим размещение c его днями и услугами по его id var res = await dbService.Reservations.GetByIdWithDaysAndServices(1);
Console.WriteLine(); Console.WriteLine($"Размещение с {res.StartDate.ToShortDateString()} по {res.EndDate.ToShortDateString()}"); Console.WriteLine(new string('=', 70));
int dayNumber = 1; int servNumber = 1; foreach (var day in res.Days) { Console.WriteLine(); Console.WriteLine($"День {dayNumber} - {day.Date.ToShortDateString()}");
foreach (var service in day.Services) { Console.WriteLine(); Console.WriteLine($"Услуга {servNumber}:{service.Name} за {service.Cost}руб."); servNumber++; }
Console.WriteLine(new string('-', 30)); dayNumber++; servNumber = 1; }
}
private static async void AddReservation(SqliteDataService dbService) { //создаем дни размещения ReservedDay day1 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 20), Cost = 200M }; ReservedDay day2 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 21), Cost = 200M }; ReservedDay day3 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 22), Cost = 200M };
//размещение и его дни Reservation res = new Reservation() { StartDate = day1.Date, EndDate = day3.Date, Status = 1 }; res.Days.Add(day1); res.Days.Add(day2); res.Days.Add(day3);
//добавляем и сохраняем в БД await dbService.Reservations.Add(res); await dbService.Commit(); }
private static async void AddServicesToReservation(SqliteDataService dbService) { var serv1 = new AdditionService { Name = "Бассейн", Cost = 2M }; var serv2 = new AdditionService { Name = "Парилка", Cost = 1M }; var serv3 = new AdditionService { Name = "Завтрак в номер", Cost = 100M }; var serv4 = new AdditionService { Name = "Чай в номер", Cost = 10M };
//находим размещение по его id var res = await dbService.Reservations.GetByIdWithDays(1);
//выбираем дни по дате var dateDay1 = new DateTime(year: 2017, month: 8, day: 20); var dateDay2 = new DateTime(year: 2017, month: 8, day: 21); var dateDay3 = new DateTime(year: 2017, month: 8, day: 22); var day1 = res.Days.Single(d => d.Date == dateDay1); var day2 = res.Days.Single(d => d.Date == dateDay2); var day3 = res.Days.Single(d => d.Date == dateDay3);
//добавляем услуги в эти дни day1.Services.Add(serv1); day1.Services.Add(serv4);
day2.Services.Add(serv3); day2.Services.Add(serv1); day2.Services.Add(serv2);
day3.Services.Add(serv4);
//запоминаем await dbService.Commit(); } }
Целиком пример можно скачать здесь

понедельник, 12 ноября 2018 г.

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

У меня есть расширение файла. Надо получить изображение, которое является иконкой для данного расширения в операционной системе пользователя. Как это можно сделать? Файла как такового у нас нет, известно только его расширение.


Ответ

Это невероятно, но я нашёл нужную Вам фичу в Джаве. Под Виндоус работает. Под Макосью сейчас нет возможности проверить, но догадываюсь, что тоже работать будет. Под Линуксом всё плохо :(.

суббота, 13 октября 2018 г.

Как открыть файл или папку из java-приложения?

Каким образом можно открыть файл или папку, как будто мы открываем его через стандартный файловый браузер / Finder / и т.д.


Ответ

Стоит обратить внимание на Desktop API. Вот статья, подробно описывающая его.
Инициализация:
Desktop desktop = null; if (Desktop.isDesktopSupported()) { desktop = Desktop.getDesktop(); }
Открытие файла:
try { desktop.open(new File(fileName)); } catch (IOException ioe) { ioe.printStackTrace(); }