Konverze časové zóny ve webové službě

Datový typ DateTime má v sobě položku DateTimeKind, která může nabývat hodnot Local, Utc, Unspecified. Podle nastavení této hodnoty je po-té provedena konverze časové zóny při volání webové služby.

V případě kindu unspecified se konverze časové zóny neprovádí. V případě kindu local se převede časová zóna volající strany do časové zóny příjemce. V případě kindu UTC se čas také nepřevádí.

Kind se při přenosu nemění.

Zde jsou příklady, jak která operace v C# nastavuje DateTimeKind:

DateTime dt1 = DateTime.Now; // DateTimeKind.Local
DateTime dt2 = DateTime.UtcNow; // DateTimeKind.Utc
DateTime dt3 = new DateTime(2014, 1, 1); // DateTimeKind.Unspecified
DateTime dt4 = DateTime.Now.ToLocalTime(); // DateTimeKind.Local
DateTime dt5 = DateTime.Now.ToUniversalTime(); // DateTimeKind.Utc
DateTime dt6 = DateTime.SpecifyKind(DateTime.Now.ToUniversalTime(), DateTimeKind.Unspecified); // DateTimeKind.Unspecified, pouze změna kindu bez konverze času

Při přenosu času jsme nakonec použili toto řešení:

Struktura:
...
public class WorkLogItemDTO
{
  /// <summary>
  /// Datum pracovního záznamu.
  /// </summary>
  public DateTime Datum { get; set; }

  /// <summary>
  /// Identifikátor klientské časové zóny.
  /// </summary>
  public string TimeZoneID { get; set; }
...
 
Klient (při volání serveru):
...
dto.Datum = item.Datum;
dto.TimeZoneID = TimeZoneInfo.Local.Id;
...

Server:
...
if (item.Datum.Kind != DateTimeKind.Unspecified)
{
  if (string.IsNullOrEmpty(item.TimeZoneID))
  {
    throw new Exception("Casova zona neni vyplnena (WorkLogItemDTO->IdTimeZone).");
  }
  TimeZoneInfo clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(item.TimeZoneID);
  item.Datum = TimeZoneInfo.ConvertTime(item.Datum, clientTimeZone);
}
...

 

Azure WebJob se občas spouští dvakrát

Problém

Máme v Azure nasazeno několik Azure Website spolu s naplanovanými (scheduled) web job. Řešíme situaci, kdy se webjob spustí dvakrát, zhruba v rozmezí jedné minuty. (V našem konkrétním případě posíláme notifikační emaily a tyto nám přijdou dva.)

Analýzou historie zjišťuji, že scheduler se webjob pokusil úlohu spustit dokonce třikrát.

webjob-history

Přičemž log jednotlivých pokusů vypadá takto:

  1. Http Action – Request to host ‚(…).scm.azurewebsites.net‘ failed: The timeout (30 seconds) was reached.
  2. Http Action – Response from host ‚(…).scm.azurewebsites.net‘: ‚Conflict‘ Response

    Body: Cannot start a new run since job is already running.
  3. Http Action – Response from host ‚(…).scm.azurewebsites.net‘: ‚Accepted‘ Response

Položky logu interpretuji takto:

  • Bod 1 úspěšně spustit webjob, ale nebyl schopen v intervalu 30 sekund toto zjistit.
  • Bod 2 se pokusil spustit webjob, ale ten již běžel, takže byl oznámen konflikt.
  • Bod 3 se opět pokusil spustit webjob, ten již doběhl, takže byl webjob úspěšně (ve skutečnosti podruhé) spuštěn.

Řešení

Po přečtení

se pokouším interpretovat (snad) klíčovou větu „If your site runs continuous web jobs, you should enable Always On, or the web jobs may not run reliably“ tak, že Always On zapínám i pro náš scheduled Web Job. Pomáhá to.

Volba Always On se nastavuje v Azure portálu, v nastavení Azure Website, se přepínačem Always On na záložce Configure.

Přístup k Team Foundation Serveru z Azure Website

Problém

V jedné z aplikací přistupujeme pomocí API jako klient do Team Foundation Serveru 2013.

ICredentials credentials = new NetworkCredential(...);
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), credentials);
collection.EnsureAuthenticated();

Po migraci aplikace z vlastních serverů do Azure Website nám připojení přestalo fungovat, konkrétně začalo docházet k této výjimce:

System.IO.IOException: The specified registry key does not exist.
   at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
   at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
   at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorageHelper.GetRootKey(String subkeyName)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorage.RetrieveToken(VssTokenKey tokenKey)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.VssTokenStorage.Retrieve(VssTokenKey tokenKey)
   at Microsoft.TeamFoundation.Client.TfsClientCredentialStorage.RetrieveToken(Uri serverUrl, VssCredentialsType credentialType)
   at Microsoft.TeamFoundation.Client.CookieCredential.OnCreateTokenProvider(Uri serverUrl, HttpWebResponse response)
   at Microsoft.TeamFoundation.Client.IssuedTokenCredential.CreateTokenProvider(Uri serverUrl, HttpWebResponse response, IssuedToken failedToken)
   at Microsoft.TeamFoundation.Client.TfsClientCredentials.TryGetTokenProvider(Uri serverUrl, IssuedTokenProvider&amp; provider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.PrepareWebRequest(HttpWebRequest webRequest, Guid sessionId, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken&amp; currentToken, IssuedTokenProvider&amp; tokenProvider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.CreateSoapRequest(Uri requestUri, Guid sessionId, String soapAction, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken&amp; currentToken, IssuedTokenProvider&amp; tokenProvider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.CreateWebRequest()
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequest()
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestChannel.Request(TfsMessage message, TimeSpan timeout)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpClientBase.Invoke(TfsClientOperation operation, Object[] parameters, TimeSpan timeout, Object[]&amp; outputs)
   at Microsoft.TeamFoundation.Framework.Client.Registration.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationProxy.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshMemoryCache()
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshCachesIfNeeded(Boolean direct)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.FindServiceLocation(String serviceType, String toolId)
   at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.LocationForCurrentConnection(String serviceType, Guid serviceIdentifier)
   at Microsoft.TeamFoundation.Client.TfsConnection.EnsureProviderConnected()
   at Microsoft.TeamFoundation.Client.TfsConnection.EnsureAuthenticated()

TFS klient v průběhu přihlašování šahá do registrů. Po dlouhém pátrání pomocí .NET Reflectoru jsem dospěl k názoru, že v nich hledá dříve uložený autentizační token. To pro nás není podstatné, důležité je, že se bez toho můžeme obejít.

Řešení

Řešením je použití třídy TfsClientCredentials. Tu vyrobíme pomocí WindowCredentials a tu s pomocí dvou tříd: NetworkCredentials a SimpleWebTokenCredential, přičemž v NetworkCredentials máme údaje k autorizaci a SimpleWebTokenCredential nemusí nést žádné hodnoty, pouze zajišťuje workaround popsaného problému.

NetworkCredential networkCredentials = new NetworkCredential(...)
TfsClientCredentials tfsCredentials = new TfsClientCredentials(new WindowsCredential(networkCredentials), new SimpleWebTokenCredential(null, null), allowInteractive: false);
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), tfsCredentials);
collection.EnsureAuthenticated();

iTunes (+Match) – nelze změnit Media Kind (např. na Audiobook)

Pro hudbu a audioknihy používám iTunes. Protože mám iTunes Match, cpu do toho všechny CD, co se nám doma objeví. iTunes si je grabnou do M4A a uloží do Library. Ještě do „nedávna“ šlo měnit po nacucnutí CD pomocí Get Info volbu Media Kind (sekce Options), přičemž po přepnutí na Audiobook se album přesunulo ze sekce Music do sekce Audiobooks.

Dneska jsem zjistil, že volba Media Kind mi nabízí jedinou možnost: Music. Naimportované CD s pohádkami se mi nedaří dostat mezi Audiobooks.

Nakonec se ukazuje, že to je jakési nové chování iTunes, které se projevuje ve spojení s aktivním iTunes Match, kdy nelze Media Kind změnit.

Workaround: Stačí na chvilku vypnout iTunes Match (menu: Store / Turn Off iTunes Match), potom lze Media Kind změnit. Následně iTunes Match reaktivovat. To mi funguje.

Někdo další radí založit druhou Library bez napojení na iTunes Match, do ní si grabovat, nastavit Media Kind a další tagy a následně odtud hrnout do hlavní Library.

Jak vypnout automatické stmívání LCD panelu notebooku při tmavých obrazech

Už pěknou dobu mě štve, že můj notebook při tmavých obrazech (například Visual Studio bez otevřeného souboru) automaticky stahuje intenzitu podsvícení, což zhoršuje čitelnost a po změně obrazu na světlý mu několik vteřin trvá, než se zadaptuje zpět.

Je mi jasné, že to je hlavně kvůli úspoře baterie, ale proč to dělá i na AC adaptéru, to nevím. Každopádně mě to štve natolik, že jsem se to snažil od počátku nějak vypnout. Celkem rychle jsem se (chybně) dogooglil k nastavení Intel HD Graphics, které by to mělo být:

image

Po opakovaných neúspěšných pokusech s tímto nastavením jsem si to pro sebe uzavřel s tím, že mám holt smůlu a vypnout se mi to nepodaří. (Nakonec se ukázalo, že musí být vypnuté i toto, resp. alespoň v poloze „max. jeden dílek od Maximální kvality“).

Dnes mě to po delší době zase rozběsnilo, tak jsem pátral znovu. Nakonec jsem se dopátral k volbě, která zabírá, a kterou se to dá opravdu vypnout:

image

Nakonec se ukazuje, že vypnout je potřeba obojí. Volba přes Windows Power Options je výrazně agresivnější, nicméně i s jejím vypnutím se stále trochu obraz stmívá (není už to poznat ve Visual Studiu, ale třeba SnagIt, který je celý černý, tak nakonec intenzitu stáhne). Teprve po vypnutí na obou místech přestane stmívání úplně. Různou kombinací voleb lze určitě dosáhnout i přijatelného kompromisu mezi spotřebou a koukatelností.

Azure Website: Pokus o stažení souboru *.woff souboru končí chybou 404, přestože existuje

Abych byl spravedlivý, nejde jen o Azure Website, ale i o Windows Servery 2008. Důvodem je nedefinovaný Content-Type (resp. MimeType) pro daný typ.

Ten můžeme přidat do web.configu:

<system.webServer>
<staticContent>
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
</staticContent>
</system.webServer>

Pokud však takovouto konfiguraci nasadíme na server, kde je již Content-Type (resp. MimeType) pro danou příponu nastaven, dojde k pádu aplikace. Zobrazená chyba nám oznámí, že se pokoušíme nastavit MimeType pro typ, který je již definován.

Pokud chceme mít jednu konfiguraci pro všechny serveru, ať již mají nebo nemají definovaný MimeType pro příponu woff, můžeme použít trik s remove:

<system.webServer>
<staticContent>
<remove fileExtension=".woff" />
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
</staticContent>
</system.webServer>

.NET Memory Internals 2/2 – Heap, Garbage Collector – záznam, slides a dema [MS Fest Praha 2014]

Slides z mé přednášky 30.11.2014 pro konferenci MS Fest Praha:

Z přednášky jsem pořizoval záznam, který najdete na našem HAVIT YouTube Channelu:

.NET Memory Internals 1/2 – záznam, slides a dema [MS Fest Praha 2014]

Slides z mé přednášky 29.11.2014 pro konferenci MS Fest Praha:

Z přednášky jsem pořizoval záznam, který najdete na našem HAVIT YouTube Channelu:

Jak dělám záznamy svých přednášek pro YouTube

Od určité doby nahrávám veškeré své přednášky pro HAVIT YouTube Channel. Nenahrávám stage, ale jen obrazovku a audio.

Základní setup je jednoduchý…

Short Story

Záznam obrazovky pomocí Camtasie, audio nahrávám pomocí klopového mikrofonu do iPhonu, v Camtasii to spojím v bodě tlesknutí a vyexportuju do MP4 pro YouTube.

TechSmith Camtasia pro záznam obrazovky

image

Konkrétně Camtasia Recorder:

          • v režimu Full Screen
  • se záznamem z vestavěného mikrofonu v notebooku (je dobré si dopředu vyzkoušet) – pro některé „nepohyblivé“ přednášky stačí, jinak mi poskytuje synchronizační klapku se samostatným audiozáznamem (viz dále)
  • jediná záludnost – nesmíte pustit Camtasia Recorder, dokud se nepřipojíte na projektor a neusadíte si rozlišení. Jinak se můžete dočkat nepříjemností, jako já letos na brněnském MS Festu. Jak jsem dodatečně analyzoval, zapnutý Recorder blokoval změny rozlišení, a mě se zoufale v časovém presu nedařilo s projektorem uspokojivě skamarádit.
  • licenci Camtasie mám jako MVP od TechSmith bezplatně, alternativně lze použít Windows Media Encoder (ale sám jsem nikdy nezkoušel).

Rode smartLav mikrofon + iPhone pro záznam zvuku

image

Velmi kvalitní „klopový“ mikrofon:

  • v zapojení do iPhone telefonu (pro přednášku vždy přepínám do Flight modu),
  • se záznamem do aplikace Rode Rec (ale úplně v pohodě to fungovalo i do předinstalované aplikace Diktafon)
  • nahrávám v nastavení 11025 Hz, Mono (vychází to kolem 20 MB/hod),
  • mikrofon nedávat ke krku, ale spíš někam na prsa/solar, citlivý je dost,
  • důležité je před záznamem nastavit/zkontroloval leveling záznamu, obvykle je citlivost vytočena na maximum, a pokud by se to nechalo, záznam bude téměř nepoužitelný (Jednou jsem musel použití i původní obyčejný záznam z vestavěného mikrofonu notebooku, když jsem na toto zapomněl). Je potřeba stáhnout citlivost tak, aby se při mluvení nedostával ukazatel do červené zóny.
  • výstup si přes File Sharing do formátu AAC (.m4a), stahuji z iPhone přes iTunes (starší verze Camtasie M4A neuměly, bylo potřeba to prohnat přes iTunes do MP3, nyní už to jde přímo),
  • stačil by zřejmě i mnohem obyčejnější mikrofon a jakékoliv jiné záznamové zařízení k němu, které vám umožní volný pohyb. Michal Altair Valášek třeba používá stejným způsobem diktafon SONY, který položí někam zhruba do první řady.

Sestříhání v Camtasia Studiu

Celý trik následného spojení v Camtasia Studiu spočívá v použití synchronizační klapky (stejný princip, jako u filmu), v amatérských podmínkách tedy tlesknutí/lusknutí, nebo podobném zvuku na začátku záznamu, který zobrazí na audio-křivce špičku.

image

Následně je potřeba zahodit méně kvalitní audio ze záznamu obrazovky, tj. rozpojit obraz od audia (pravé tlačítko myši na stopě + Separate video and audio), stejně tak zahodit track System Audio (pokud tam speciálně něco nemáte).

Obvykle dávám ještě:

  • Enable noise removal (Adjust sensitivity = 40-50)
  • někdy pomáhá Enable volume leveling, pokud nebyl dobře nalevelovaný mikrofon při záznamu.

Produkce MP4 pro YouTube

Dle instrukcí, které jsem našel přímo na webu na YouTube a zkombinoval s vlastními požadavky, používám obvykle nastavení produkce (ve finále koukám jiné, než YouTube doporučuje :-)):

  • Editing Dimensions (= Recording Dimensions)
  • Format = MP4
  • bez Controlleru
  • ostatní nechávám default (Frame Rate = automatic, …)

Dostanu MP4 soubor (obvykle cca 100-200MB), který si archivuji a nahraju na YouTube. Zdrojové soubory nakonec zahazuji.

Na YouTube ještě většinou nahrávám vlastní „miniaturu“. První slide uložím z PowerPointu jako PNG (File / Save As…) a doplním ho v Paint.NET na velikost 1280×720 (rozměr dle doporučení YouTube).

Azure: Nedaří se vytvořit databázi importem z *.bacpac

Při pokusu o založení nové databáze importem z *.bacpac v portálu se zobrazuje potvrzení, že přístup do Azure Storage je v pořádku a toto oznámení je následováno chybou oznamující, že nelze vložit požadavek na import databáze.

Heslo k databázovému serveru obsahovalo diakritiku, nápad zkusit heslo bez diakritiky se osvědčil –  po změně hesla funguje import bez problémů.