Архив рубрики ‘.NET’

Threading in C#

Posted: Ноябрь 3, 2013 in .NET
Метки:,

Allright, you may be aware that Joe Albahari is the inventor of LINQPad. Moreover he is an author of one of the finests books about .NET in general and C# in particular. Recently I was searching throuhg the Internet for a decent article about threading in C#. And first result I was getting from Google was one chapter of his C# 4.0 in a Nutshell book. Frankly it was full and comprehensive description of old threading model and TPL which was introduced in .NET 4.0. Here is a link to his page http://www.albahari.com/threading/
threading

Реклама

У Дениса Гладких есть хорошая статья о том как передать список/коллекцию/множество в хранимую процедуру. Автор приводит три способа:

Решение 1. Строка – список значений, разделенных запятой
Решение 2. BULK INSERT
Решение 3. Table-Valued Parameters (Database Engine)

У нас в проекте использует еще одно решение… Если у вас на форме есть немного чекбоксов с выбором, то можно это все представить как число в двоичном счислении и завернуть в одну цифры в десятичном.

Пример: пусть у нас есть 5 чекбоксов с какими-то выборами (ClosedCheck, NotWorkCheck, TooSmallCheck, OthersCheck, LargeTo5CheckBox). Представим это все как одно число

int indicatorFlag = (ClosedCheck.Checked ? 1 : 0) + (NotWorkCheck.Checked ? 2 : 0) + (TooSmallCheck.Checked ? 4 : 0) + (OthersCheck.Checked ? 8 : 0) + (LargeTo5CheckBox.Checked ? 16 : 0);
cmd.Parameters.AddWithValue("@Indicator_Flag", indicatorFlag);

Со стороны встроенной процедуры имеем

@Indicator_Flag tinyint = 15 -- по умолчанию все единицы

А обращаться к отдельным битам нужно вот так

@Indicator_Flag&1 != 0
@Indicator_Flag&16 != 0

Вот такой вот способ. Похоже на стиль из олимпиадного программирования:)

У нас в проекте очень часто используются встроенные процедуры. Очень утомительно получается создавать репозитории и по нескольку раз писать одно и то же

SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "dbo.GetSomeData";
cmd.CommandType = CommandType.StoredProcedure;

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

Deals deal = new Deals { Deal_Num = agreementNum, DealType_Code = type };
IList<Deals> deals = deal.ExecuteOptionalFromProcMap<Deals>("pGetAllSubjectDeals").ToList<Deals>();
int dealId = deals.ElementAt(0).Deal_ID;

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

    class Program
    {
        static void Main(string[] args)
        {
            dynamic sql = new DynamicSql();
            dynamic results = sql.pGetAllSubjectDeals(DealType_Code: 2, Deal_Num: "90100");

            Console.WriteLine(results[0].Deal_ID);
            Console.WriteLine(results[0].Deal_Num);
            Console.ReadLine();
        }

    public class DynamicSql : DynamicObject
    {
        private dynamic _expando;

        private IDictionary<string, object> Dictionary
        {
            get
            {
                return _expando as IDictionary<string, object>;
            }
        }

        public object GetSQLParameterValue(SqlParameter param, CallInfo callInfo, object[] args)
        {
            for (int i = 0; i < callInfo.ArgumentCount; i++)
            {
                if (param.ParameterName.Equals("@" + callInfo.ArgumentNames[i]))
                {
                    return args[i] ?? DBNull.Value;
                }
            }

            return DBNull.Value;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            _expando = new ExpandoObject();

            var callInfo = binder.CallInfo;

            using (SqlCommand cmd = Command)
            {
                cmd.CommandText = "dbo." + binder.Name;

                if (cmd.Connection.State != ConnectionState.Open)
                {
                    cmd.Connection.Open();
                }

                SqlCommandBuilder.DeriveParameters(cmd);

                FixStructuredTypeName(cmd);

                cmd.Parameters
                    .Cast<SqlParameter>()
                    .ToList<SqlParameter>()
                    .ForEach(p => p.Value =
                        (p.ParameterName != "@RETURN_VALUE" ? GetSQLParameterValue(p, callInfo, args) : DBNull.Value));

                try
                {
                    SqlDataReader reader = cmd.ExecuteReader();

                    while (reader.Read())
                    {
                        RecordToExpando(reader);
                    }

                    reader.Close();

                    RecordToExpando(cmd);
                }
                catch (SqlException ex)
                {
                    Dictionary.Add("ErrorMessage", ex.ToString());
                    throw ex;
                }
                finally
                {
                    cmd.Connection.Close();
                }
            }

            result = _expando;

            return true;
        }

        /// <summary>
        /// производится проверка параметров процедуры на наличие input табличных параметров
        /// </summary>
        /// <param name="cmd"></param>
        public static void FixStructuredTypeName(SqlCommand cmd)
        {

            foreach (SqlParameter param in cmd.Parameters)
            {
                if (param.SqlDbType != SqlDbType.Structured)
                    continue;

                // param.TypeName will be database.schema.typename
                string typeName = param.TypeName;

                // Trim off the database name to get schema.typename
                typeName = typeName.Substring(typeName.IndexOf(".") + 1);

                // If Microsoft fix this in a future release and only return
                // schema.typename, we would end up with just the typename (no dot)
                // So only change the TypeName if we still have a dot in our text
                if (typeName.Contains("."))
                    param.TypeName = typeName;
            }
        }

        public void RecordToExpando(SqlCommand command)
        {
            for (int i = 0; i < command.Parameters.Count; i++)
            {
                if (command.Parameters[i].Direction == ParameterDirection.Output ||
                    command.Parameters[i].Direction==ParameterDirection.InputOutput||
                    command.Parameters[i].Direction==ParameterDirection.ReturnValue)
                {
                    Dictionary.Add(command.Parameters[i].ParameterName.Replace("@", String.Empty),
                       DBNull.Value.Equals(command.Parameters[i].Value) ? null : command.Parameters[i].Value);

                }
            }
        }

        public void RecordToExpando(IDataReader reader)
        {
            for (int i = 0; i < reader.FieldCount; i++)
            {
                Dictionary.Add(reader.GetName(i), DBNull.Value.Equals(reader[i]) ? null : reader[i]);
            }
        }

        public static SqlCommand Command
        {
            get
            {
                SqlCommand cmd = new SqlCommand("", Connection);
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.CommandTimeout = 356;
                return cmd;
            }
        }

        private static SqlConnection Connection
        {
            get
            {
                return new SqlConnection(ConfigurationManager.ConnectionStrings["BillingConnectionString"].ConnectionString);
            }
        }
    }
    }
    

DSLs in Boo

Posted: Январь 6, 2012 in .NET, DSL
Метки:,

Прочитав книжку DSLs in Boo, я захотел сделать небольшой DSL для своей предметной области. Ссылку на последние исходники можно найти в конце статьи. В самой статье я сделаю несколько комментариев.
В данный момент я работаю в компании, которая занимается снятием показаний с приборов учета специально обученными людьми на КПК с последующей отправкой на сервер. Иногда, для большего контроля, приходится просить, чтобы вместе с числовым значением показания, контролер еще делал фотографию прибора учета. Такие заявки поступают от разных людей, чаще всего они имеют вид

Прошу проставить на фотофиксацию показаний л/с ,где разница показаний с ноября по декабрь 2011г. составляет от 1кВт до 50кВт.

В целях более полной проверки достоверности снимаемых показаний прошу Вас включить 20% -ное принудительное фотографирование в январе месяце для следующих контролеров…

Прошу вас выставить 50% фотографирование на маршрут контролера Иванова И.И. № 7777 с сегодняшнего дня.

и т.д.

Все заявки имеют вид типа:

 Проставить на фотофиксацию
 с 1 января 2012 по 31 января 2012г
 для контролера Иванова И.И.
 100% фотографирование
 где никогда не производилась фотофиксация
 Перечень л/с скинуть на manager1@mail.ru

А это уже похоже на некоторый DSL. В качестве языков для построения DSL обычно выбирают языки, которые уже сами по себе немного похожи на естественный язык. В мире .NET популярностью пользуются F# и Boo, т.к. они позволяют делать вызов функций без скобок. А еще в Boo можно компилятору подсказывать что вы имели ввиду написав то, что не входит в стандартный язык. Не знаю есть ли в F# такое метапрограммирование, наверняка что-то можно придумать. Так вот, чтобы долго не думать, возьмем эту заявку в качестве эталона, заменим пробел на подчеркивание, выравняем код как в Boo и получим «программу» вида

 запрос "фотофиксация для контролера" :
    для_контролера_номер 7777
    начиная_с "01.01.2012"
    до "31.01.2012"
    установить_процент_снятия 50
    список_лицевых_выслать_на_почту "manager1@mail.ru"
    где :
       показания_не_снимались "01.12.2011", "31.12.2011"

Базовый класс в С# будет выглядеть как-то так

public abstract class BasePhotoRequest
    {
        public string Name { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public int ControllerId { get; set; }
        public int PhotoPercent { get; set; }
        public string NotifyEmail { get; set; }

        public abstract void Build();

        protected void запрос(string name, Action block)
        {
            Name = name;
            block();
        }

        public void PrintProperties()
        {
            …
        }

        public void начиная_с(string customDate)
        {
		…
        }

        public void где(Action block)
        {
            block();
        }

        public void показания_не_снимались(string date1, string date2)
        {
		//Создадим какое-нибудь правило Rule
            …
        }

        public void до(string customDate)
        {
            …
        }

        public void для_контролера_номер(int controllerId)
        {
            ControllerId = controllerId;
        }

        public void установить_процент_снятия(int percent)
        {
            PhotoPercent = percent;
        }

        public void список_лицевых_выслать_на_почту(string email)
        {
            NotifyEmail = email;
        }

    }

Еще я хотел бы завести класс Rule для блока «где». И на каждое правило создать свой класс. В конце концов я хотел бы, чтобы класс BasePhotoRequest выдавал SQL выражение, которое можно было быстренько исполнить в базе с помощью классов ADO.NET. А эти правила будут отражаться в SQL-блоке WHERE. Правила простенькие как например ограничение потребления электроэнергии

public class ConsumptionRule : Rule
    {
        private readonly double _val1;
        private readonly double _val2;

        public ConsumptionRule(double  val1, double val2)
        {
            _val1 = val1;
            _val2 = val2;
        }

        public override string ToString()
        {
            return string.Format("consumption between {0} and {1}", _val1, _val2);
        }
    }

Метод Build в BasePhotoRequest помечен как абстрактный. Здесь произойдет немного магии. Компилятор в Boo как конвейер: состоит из нескольких шагов. Мы этому компилятору скажем, чтобы на первом шаге он заталкал код из Boo в метод Build. Делается это таким образом

public class MyDslEngine: DslEngine
    {
       public MyDslEngine()
        {
           Storage = new FileSystemDslEngineStorage();
        }

        protected override void CustomizeCompiler(BooCompiler compiler,
                                                  CompilerPipeline pipeline,
                                                  string[] urls)
        {
            compiler.Parameters.Ducky = true;

             pipeline.Insert(1,
                            new AnonymousBaseClassCompilerStep(
                                typeof(BasePhotoRequest),
                                "Build",
                                "ConsoleApplication1"
                                ));
        }
    }

Далее где-нибудь в программе сделаем вызов

DslFactory dslFactory = new DslFactory();
dslFactory.Register<BasePhotoRequest>(new MyDslEngine());
dslFactory.BaseDirectory = Environment.CurrentDirectory;
var photo = dslFactory.TryCreate<BasePhotoRequest>("photo.boo");
photo.Build();

И наш класс BasePhotoRequest будет создан. На самом деле компилятор Boo с помощью библиотеки Rhino.DSL создаст нам класс-наследник от BasePhotoRequest (вот она польза от многоязычности CLR исполняющей все на IL), в метод Build засунет наш код, и вернет этот класс обратно. Мы лишь вызовем сам метод Build.
Наверное стоит сказать, что автор это книги также является автором библиотеки Rhino.DSL, которая включает в себя все рутины по созданию анонимных классов и настройки компилятора.
Ну вот мы создали какой-то DSL. Как теперь им пользоваться? Чтобы давать другим людям непрограммистам писать на нем, нужна какая-то среда. А то могут подумать, что можно писать что угодно через подчеркивание и программа все сделает как надо. В качестве редактора кода возьмем редактор кода от SharpDevelop. В нем легко настраивается подсветка и даже IntelliSense.
Для настройки подсветки нужно создать файл, например Dsl.xshd, и в нем все ключевые слова прописать. Для форматирования — отнаследовать от DefaultFormattingStrategy. Для IntelliSense — отнаследовать от ICompletionDataProvider. Заключительный скриншот редактора

Не уверен, что все это сильно жизнеспособно, ну да ладно. Хотел немного поиграться;)

Ссылка на исходники

Изначально интерфейс был очень страшный

До того момента как потребовалось передать ее заказчику. Интерфейс был переделан на

Вот и все. Бугагашенька!

Video.Show

Судя по всему это приложение писалось от силы неделю. Тем не менее здесь очень наглядный и простой дизайн.
BLL — обертка вокруг Data. Попытка фасада, но больше как просто обертка
Data — общается с базой, возвращает объекты из Model (автор и не называет это DAL-ом)
Model — объекты предметной области. Пустышки с одними свойствами и linq2sql классы.
VideoProcessingService — отдельный сервис по обработке видео
Web — веб-морда приложения

mojoportal

Довольно большая система. Чувствуется добротный old school.
mojoPortal.Business — объекты предметной области. Общаются с объектами из mojoPortal.Data.xxx, которые по сути являются репозиториями возвращающими IDataReader (в простонародье DAL). Здесь IDataReader последовательно считывается и возвращаются соответсвующие объекты.
mojoPortal.Business.WebHelpers — no comment
mojoPortal.Data.xxx — DAL. Интересно, что все проекты для разных баз имеют одни и те же namespace, в следствии чего из можно подменять просто меняя dll.
mojoPortal.Features.Business — то же что и mojoPortal.Business, только для блогов, календарей, форумов…
mojoPortal.Features.Data.xxx — здесь тоже понятно
mojoPortal.Features.UI — aspx блогов, форумов…
mojoPortal.Net — несколько классов для инфраструктурных вопросов по работе почты
mojoPortal.Web — веб-морда приложения

CommonLibrary.CMS

Чисто индусская CMS с огромный количеством кода. Зиждется на небезызвестном CommonLibrary.NET
CommonLibrary.CMS — веб-морда приложения. Все фичи/плагины/модули а-ля форумы, блоги… находятся здесь
CommonLibrary.CMS.Console — генерация классов для встроенного ORM-а
CommonLibrary.Web.Lib — контроллеры, ViewModel…
CommonLibrary.Web.Modules — объекты предметной области, конроллеры, сервисы, хелперы о_0
CommonLibrary — виновник торжества. Об этой библиотеке я писал ранее. Если коротко, то это огромное сборище велосипедов начиная от классов для работы с RSS и заканчивая почти настоящим ORM-ом. Просто куча классов, которые умеют что-то делать (здесь акцент идет именно на поведение, а не хранение данных).
В общем не самая образцовая архитектура, хотя почитать исходники было довольно интересно. Не понятно, почему дизайн такой сумбурный, потому что в блоге создателя CommonLibrary.CMS я нашел две типовые архитектуры

Typical Multi-Layer
Structure 1

  • Application.Core
  • Application.Data
  • Application.Service
  • Application.WebServices
  • Application.UI
  • Utilities
  • Utilities.Providers
  • Utilities.UnitTests
  • Appication.UnitTests

Modular ( Forums, Articles )
Structure 2

  • Application.Core
  • Application.Common
  • Application.Forums
  • Application.Articles
  • Application.CalendarEvents
  • Utilities
  • Utilities.Providers
  • Utilities.UnitTests
  • Application.UnitTests

Одну прикольную структуру я недавно рассматривал — это был MUD Designer. Там автор интуитивно хорошо все разделил на полезные абстракции, не зная терминов DDD. Почему интуитивно? Потому что зная хотя бы набор слов, который используется в предметной области самого DDD, можно смело выделять сущности, объекты, сервисы, репозитории, спецификации, иметь ограниченные контексты…

SmartCA

Автор так был впечатлен книгой Нильссона по DDD, что решил слабать прогу на эту тему и под шумок выпустить свою книгу, где больше половины занимает распечатанный код этой проги.
SmartCA.Application — содержит только UserSession
SmartCA.DataContracts — DTO
SmartCA.DataContracts.Helpers — no comment
SmartCA.Infrastructure — интерфейсы, транзакции, unit of work
SmartCA.Infrastructure.Membership
SmartCA.Infrastructure.Membership.Providers
SmartCA.Infrastructure.Repositories — репозитории
SmartCA.Infrastructure.Specifications — спецификации
SmartCA.Infrastructure.Synchronization — no comment
SmartCA.Infrastructure.UI — немного ViewModel и DelegateCommand
SmartCA.Model — святая святых, бизнес модель, правда некоторые классы засорены методами валидации. здесь же находятся сервисы, частично дублирующие API соотвествующих репозиториев.
SmartCA.Presentation — WPF
В общем все эти примеры DDD довольно наивные, но взглянуть на них конечно же стоит.

jforum

actions — что-то наподобии контроллеров из MVC. Что-то делают, а затем redirectToAction.
api — Used to execute code inside JForum environment (даже не разбирался)
core — несколько базовых классов: события, ошибки… Здесь же располагаются DAO классы реализующие соответствующие интерфейсы репозиториев (wtf).
entities — pojo-сущности. Правда являются отображение таблиц с соответствующей разметкой.
events — события, которые происходят с сущностями в различные моменты работы с БД
extensions
formatters — классы для BBCode
plugins — плагины, например, для поддержки тегов. Классы находятся полным набором: entity, dao, service, repository…
repository — интерфейсы репозиториев
security — правила доступа, разновидность спецификации
services — различные domain сервисы. Опять же частично дублируют API репозиториев
sso — классы для поддержки технологии Single Sign On
util — no comment
Код довольно добротный и использует целую плеяду технологий. Вообще Java программисты более искушенные в плане различных шаблонов и фреймворков.

Mud Designer

Posted: Июль 31, 2011 in .NET

About

Речь пойдет об исходниках одного итересного Mud-движка http://muddesigner.codeplex.com Не то чтобы он сильно классный, но что-то меня в нем зацепило.

Logger

Простейший логер. Конечно есть NLog, log4j, хороший логер у Enterprise Library… Но всегда так хочется создать свой простейший логер.

Client-Server

Клиент и сервер на сокетах. Сервер содержит серверный поток, который создает клиентские потоки, список BaseCharacter и соответствующий список клиентских потоков, которые слушают, что им посылают BaseCharacter (_players[sub].Receive(_players[sub].ReadInput())). Здесь инфраструктурная логика смешивается с игровой: у BaseCharacter есть свой Socket, через который он может подключаться, отключаться и посылать команды.

Script Engine

Очередной этюд на тему плагинов. Просто берет все исходники из определенной папки, и компилируют в библиотеку. Конечно, проще было бы использовать какой-нибудь Ioc-контейнер, но ведь это не так интересно, как свой велосипед. Кстати, этот скриптовый движок выделен в отдельный проект http://rscript.codeplex.com/

MUD Engine — игровой мир.

GameManagement

Есть Game, который отвечает за инфраструктуру (Start, Save, Shutdown, IsMultiplayer). Есть GameWorld — это собственно сам игровой мир: локации, монстры… SecurityRoles — роли. Интересная сущность — GameTime: нигде не используется, но полезно иметь такую абстракцию. Также в игре есть система команд. Каждая команда реализует IGameCommand.

Characters

BaseCharacter — класс персонажа. Других классов в движке замечено не было. Понятно, что этот класс сделан из расчета на расширение. Среди основных статов присутствуют Strength, Dexterity, Constitution, Intelligence, Wisdom, Charisma. Также у него есть чувства! Персонаж умеет себя сохранять (опять инфраструктурные вещи). У персонажа есть Inventory, где хранятся BaseItem. BaseCharacter является AggregateRoot, то есть содержит в себе Inventory. Очевидно, что Inventory должен быть идентифицирован, при этом он никому не интересен без персонажа.

Environment

Включает в себя Realm, которые содержат Zone, которые содержат Room, в которых есть Door. Также есть StartingLocation и TravelDirections. Realm, Zone и Room наследуют от BaseObject, соответственно также умеют себя сохранять. BaseObject является Entity-классом. Door — это простой Value-класс, даже не Entity.

FileSystem

Здесь находятся инфраструктурные классы по сохранению/восстановлению игры. По сути FileManager выполняет роль репозитория.

Conclusion

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