Ответы пользователя Чак Норрис - CodeHelper

Чак Норрис

Чак Норрис
Карма 3384
Имя Неизвестно
Возраст Неизвестно
Сайт Неизвестно
Twitter Неизвестно
Участвует в проекте 1104 дн., 17 час., 4 мин.
Последняя активность 536 дн., 1 час., 55 мин. назад
О себе ничего не указано

Ответы

Новые Лучшие
1
2
3
4
5
6
...
12
Перейти к вопросу →

На самом деле вопрос сложнее чем кажется. Понятно, что если задается какая-то константная коллекция, то лучше определить ее массивом, а не списком. Однако, есть мнение, что массивы можно (и даже нужно) использовать более активно, в частности, возвращать из различных сервисов и репозиториев.

Например, рассмотрим два варианта одного и того же репозитория. Со списком:

// Вариант A
public interface ICustomerRepository {
    IList<Customer> GetAll();
}

С массивом:

// Вариант B
public interface ICustomerRepository {
    Customer[] GetAll();
}

Для варианта со списком имеем следующие преимущества:

  • возвращается объект, реализующий интерфейс (IList<>), а не экземпляр конкретного класса. Значит кокретный репозиторий может вернуть специфическую реализацию списка. Так, NHibernate может вернуть коллекцию с возможностью загрузки по требованию и тд.
  • Возвращается объект, удобный для клиента — списки легко сортировать, фильтровать и тд.

Но есть один недостаток, связанный с парадигмой императивного программирования — возвращаемый список является изменяемым (mutable), клиент легко может добавить/удалить элементы в списке. Но что произойдет если клиент скажем добавит элемент в список:

var allCustomers = CustomerRepository.GetAll();
allCustomers.Add(new Customer());

Добавится ли при этом объект new Customer() в репозиторий? Изменится ли при этом внутреннее состояние самого репозитория? Интерфейс (A) не дает ответы на эти вопросы.

Преимущества варианта с массивом:

  • Возвращается легковесный и наиболее простой объект;
  • Вариант работает даже в доисторических версиях .NET;
  • Возвращается относительно неизменяемый объект (immutable). Клиент все еще может изменить элементы массива, но его длинна остается постоянной. Это конечно не решение проблемы, но все же массив считается «более неизменяемым» чем список. То есть интерфейс (B) лучше отображает сущность метода GetAll — вернуть список объектов для чтения.
Перейти к вопросу →

Похоже что в спецификациях SQL ничего нет про выборку случайных строк из таблицы. Тем не менее большинство движков баз данных поддерживает такую возможность, правда каждый по своему. Идея такая — используется сортировка не по полю, а по некоторому случайному числу, сгенерированному для каждой строки таблицы. Если взять первые n строк такой выборки, то они и будут содержать n случайных строк таблицы. Вот примеры SQL запросов:

MySql:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

PostgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Microsoft SQL Server:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

IBM DB2:

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Oracle:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

Примеры взяты из статьи SQL to Select a random row from a database table. Во всех примерах выбирается одна случайная запись, но запросы легко обобщить на случай n записей.

Перейти к вопросу →

Вот несколько extension`ов из Mono.Rocks:

Повторение действия несколько раз:

6.Times (i => Console.WriteLine (i));

Удобная реализация циклов for с инкрементом и декрементом:

6.UpTo (12, i => Console.WriteLine (i));
12.DownTo (6, i => Console.WriteLine (i));

А вот цикл Step спервого взгляда выглядит абсолютно нечитабельным:

5.Step (9, 2, i => Console.WriteLine (i));

Без сигнатуры и не разберешься

public static void Step (this int self, int limit, int step, Action<int> action)

Выполнение действия над каждой из строк многострочного текста:

@"
one
two
three".EachLine (line => Console.WriteLine (line));

Преобразование строки в значение enum определенного типа:

"Bar".ToEnum<Foo>()

Создание строки пути к элементу файловой системы из нескольких составляющих — намного удобнее чем вкладывать Path.Combine в себя несколько раз:

var path = new [] {"/home", "jb", "sources"}.PathCombine ();

Получение атрибутов для типа. Фактически можно использовать не только для типа, но и для свойства/метода/поля и тд:

var attribute = typeof (Bar).GetCustomAttribute<FooAttribute> ();
var attributes = typeof (Bar).GetCustomAttributes<FooAttribute> ();
Перейти к вопросу →

Тут есть несколько вариантов. Кроме функции @@IDENTITY, о которой написал alexey-algel можно также использовать SCOPE_IDENTITY и IDENT_CURRENT.

IDENT_CURRENT — функция возвращает идентификатор последней записи, добавленной в определенную таблицу. Функция привязана именно к таблице и не привязана к области действия или сеансу. Таким образом следующие два запроса вернут одни и те же результаты:

SELECT IDENT_CURRENT("some_table")
SELECT MAX(id) FROM some_table

@@IDENTITY — возвращает идентификатор последней записи, добавленной в текущем сеансе, в любую таблицу.

SCOPE_IDENTITY — возвращает идентификатор последней записи, добавленной в текущем сеансе и в текущей области, в любую таблицу.

С функцией IDENT_CURRENT все понятно — она возвращает id только для указанной таблицы. А вот грань между @@IDENTITY и SCOPE_IDENTITY очень тонкая. Чтобы ее почувствовать можно рассмотреть классический пример. Допустим, в базе есть таблица A. На вставку записи в таблицу A установлен триггер, который добавляет запись в другую таблицу B. Тогда, если выполнить скрипт

INSERT (...) INTO A VALUES (...) SELECT SCOPE_IDENTITY

То получим идентификатор новой записи таблицы A. Если же выполним

INSERT (...) INTO A VALUES (...) SELECT @@IDENTITY

То получим аналогичный идентификатор из таблицы B. Так происходит потому, что триггер срабатывает в том же сеансе, что и вставка записи, но в другой области. Соответственно, функция @@IDENTITY видит все insert'ы сеанса, а SCOPE_IDENTITY — только текущий insert области.

Перейти к вопросу →

Действительно, методы Send и SendAsync не подойдут. В MSDN открытым текстом говорится:

Если вызвать SendAsync или Send при выполняемой передаче электронной почты, возникнет исключение InvalidOperationException.

Единственный способ это преодолеть — создавать новый объект SmtpClient при каждой новой отправке письма. Например так:

public void SendMail()
{
    try
    {
        ThreadPool.QueueUserWorkItem(state =>
                                         {
                                             var message = GetMessage();
                                             CreateSmtpClient().Send(message);
                                         });
    }
    catch (Exception e)
    {
        throw new MailSendingException(e);
    }
}
Перейти к вопросу →

Есть очень интересная статья Dude, where's my business logic? и ее перевод на русский — Где наша бизнес-логика, сынок?. В статье рассказывается об эволюции архитектуры приложений от однозвенного до n-звенного. В контексте этой темы поднимается вопрос о том, что такое бизнес-логика и в каком слое она должна находиться. Автор рассказывает, что когда появилась двухзвенная архитектура, то перемещение логики работы с данными в хранимые процедуры способствовало повышению производительности приложений. Однако, вместе с кодом для работы с данными часто в хранимки может попасть и бизнес-логика, а это очень плохо.

Базе данных не должно быть дела до того, что такое бизнес-сущность, она должна заботиться только об элементах, используемых для хранения этой бизнес-сущности. У базы данных не должно быть возможности разобраться, какие таблицы должны хранить эту сущность, и она должна работать с таблицами не обращая внимания на бизнес-объект. Задача базы данных – хранить строки в таблицах, которые описывают объект. Кроме базовых ограничений вроде каскадной целостности, типов данных, индексов и пустых значений, база данных не должна иметь функционального знания о том, что же из себя представляет объект в бизнес слое.

Хранимые процедуры, если они есть, должны оперировать только одной таблицей; исключение – это процедуры запрашивающие выборку из нескольких таблиц для выдачи данных. В этом случае, хранимые процедуры работают как представления (view). Представления и хранимые процедуры должны использоваться для консолидации значений, но исключительно для более быстрой и эффективной работы с данными в бизнес слое.

Автор отмечает следующие недостатки присутствия логики в хранимых процедурах:

  • Трудности при переходе на другие СУБД.
  • Сложность модификации и отладки из-за того, что логика распределена по нескольким слоям.
  • Вместе с логикой хранения данных в хранимые процедуры может подмешаться бизнес-логика. Это происходит потому, что проектировщику может показаться «проще» и «логичнее» сделать именно так.

Однако, есть и исключения:

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

Перейти к вопросу →

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

<system.serviceModel>
   <services>         
     <service name="FooService"
       behaviorConfiguration="ServiceBehavior">
      <endpoint address="АДРЕС_СЕРВИСА" binding="netTcpBinding" 
        contract="IFooService" />
      <endpoint address="mex" binding="mexTcpBinding"
           contract="IMetadataExchange" />
     </service>
   </services>
 </system.serviceModel>

Затем, при создании прокси на клиенте нужно использовать тот же адрес. Например, при использовании ChannelFactory это может выглядеть так:

IFooService service = 
    ChannelFactory<IFooService>.CreateChannel(
        new NetTcpBinding(), 
        new EndpointAddress(
        "net.tcp://example.com/АДРЕС_СЕРВИСА"));

Или можно прописать то же в файле конфигурации клиента:

<system.serviceModel>
    <client>
        <endpoint 
            address="net.tcp://example.com/АДРЕС_СЕРВИСА"
            binding="netTcpBinding" bindingConfiguration="" 
            contract="Services.IFooService"
            name="FooSvc" />
    </client>
</system.serviceModel>
Перейти к вопросу →

Можно реализовать простейший алгоритм Фишера-Ятеса (Fisher–Yates shuffle). Говорят даже, что этот алгоритм упоминается в книгах Кнута. Вот реализация через extension:

public static void Shuffle<T>(this IList<T> list)  
{  
    Random random = new Random();  
    int n = list.Count;  
    while (n > 1) {  
        n--;  
        int k = random.Next(n + 1);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}
Перейти к вопросу →

Можно использовать такой вариант:

if (typeof someVariable !== 'undefined') {
    // если попали сюда, то переменная существует
}
Перейти к вопросу →

У ВКонтакте есть два класса API:

  • Open API (javascript).
  • Обычные API (не javascript).

На чистом Open API по сути можно сделать только авторизацию пользователя. Всё остальное делается на олдскульном API. Чтобы вызывать методы обычного API в Javascript Open API предусмотрен специальный мост.

Итак, если произведена инициализация:

<div id="vk_api_transport"></div>
<script src="http://vkontakte.ru/js/api/openapi.js" type="text/javascript" charset="windows-1251"></script>
<script type="text/javascript">
  VK.init({
    apiId: ВАШ_API_ID,
    nameTransportPath: "<путь от корня сайта>/xd_receiver.html"
  });
</script>

...и авторизация пользователя:

VK.Auth.login(function(response) {
  if (response.session) {
    /* Пользователь успешно авторизовался */
    if (response.settings) {
      /* Выбранные настройки доступа пользователя, если они были запрошены */
    }
  } else {
    /* Пользователь нажал кнопку Отмена в окне авторизации */
  }
});

То можно использовать мост для вызова API. Этот мост представлен объектом VK.Api. Пример использования:

VK.Api.call('getVariable', {key: 1281}, function(r) {
  if(r.response) {
    alert('Привет, ' + r.response);
  }
});

Именно метод VK.Api.call используется для вызова любого метода стандартного (не Open) API. Для запроса персональных данных (в примере запрашивается имя, фамилия, никнейм, дата рождения) можно использовать такой код:

function getUserProfileData(uid) {
    var code;
    code = 'return {'
    code += 'me: API.getProfiles({uids: "' + uid + '", fields: "first_name, last_name, nickname, bdate"})[0]';
    code += '};';
    VK.Api.call('execute', { 'code': code }, getUserProfileDataCallback);
}

function getUserProfileDataCallback(data) {
    data.response.me.first_name; // имя
    data.response.me.last_name;  // фамилия
    data.response.me.nickname;   // никнейм
    data.response.me.bdate;      // дата рождения
}

1
2
3
4
5
6
...
12
v1.7.123.556
© 2009—2010 CodeHelper FAQ | О сайте | Обратная связь | История изменений | Статьи
Creative Commons LicenseМатериалы сайта распространяются под лицензией Creative Commons Attribution-Share Alike 3.0 Unported.